-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2013,2014 Genome Research Ltd.
-
# The classes within this namespace are responsible for defining the abilities of the user and the application
-
# that are accessing the API.
-
#
-
#--
-
# To maintain the behaviour of the API before this was introduced the logic is very straight-forward:
-
#
-
# 1. If the application is authorised then everything is possible.
-
# 2. If the application is unauthorised then the user capabilities take priority.
-
#
-
# There are several pieces of functionality that are always accessible:
-
#
-
# - UUID lookups are always available
-
# - Searches are always available
-
# - Submissions can be created and updated
-
# - Sample manifests can be created through studies and updated individually
-
#
-
# In the future we'll be able to adjust this and get the right behaviour based on the combination of the
-
# application and the user. We'll also be able to extend the application abilities so that they are refined
-
# for certain applications.
-
#++
-
module Core::Abilities
-
def self.create(request)
-
CompositeAbility.new(request)
-
end
-
-
module ActionBehaviour
-
# Modify the behaviour so that we can only access the action if the ability permits and the super
-
# implementation permits it too.
-
def accessible_action?(handler, action, request, object)
-
request.ability.can?(action, handler, object) and super
-
end
-
private :accessible_action?
-
end
-
-
class CompositeAbility #:nodoc:
-
attr_reader :user, :application
-
private :user, :application
-
-
def initialize(request)
-
@user, @application = User.new(request), Application.new(request)
-
@application.authenticate!(@user)
-
end
-
-
def can?(*args, &block)
-
application.can?(*args, &block) or user.can?(*args, &block)
-
end
-
end
-
-
class Base
-
class Recorder #:nodoc:
-
def initialize
-
3
@recorded = []
-
end
-
-
def play_back(target)
-
@recorded.each { |block| target.instance_eval(&block) }
-
end
-
-
def record(&block)
-
3
@recorded << block
-
end
-
end
-
-
module ClassMethods
-
def recorder_helper(name)
-
line = __LINE__ + 1
-
singleton_class.class_eval(%Q{
-
1
def #{name}(&block)
-
3
record(@#{name} ||= Recorder.new, &block)
-
end
-
}, __FILE__, line)
-
end
-
-
def record(recorder, &block)
-
6
recorder.tap { |recorder| recorder.record(&block) if block_given? }
-
end
-
private :record
-
end
-
-
require 'cancan'
-
include ::CanCan::Ability
-
extend ClassMethods
-
-
recorder_helper(:full)
-
recorder_helper(:unregistered)
-
-
def initialize(request)
-
@request = request
-
abilitise(:unregistered)
-
abilitise(privilege) if registered?
-
end
-
-
def abilitise(name)
-
self.class.send(name).play_back(self)
-
end
-
private :abilitise
-
end
-
-
class User < Base
-
unregistered do
-
# The API is designed to be read-only, at least.
-
can(:read, :all)
-
end
-
-
recorder_helper(:authenticated)
-
-
authenticated do
-
# Submissions should be createable & updateable by anyone
-
can(:create, Endpoints::Submissions::Model)
-
can(:create, Endpoints::OrderTemplates::Instance::Orders)
-
can(:update, Endpoints::Orders::Instance)
-
can(:create, Endpoints::Submissions::Instance::Submit)
-
can(:update, Endpoints::Submissions::Instance)
-
-
# Sample manifests should also be createable & updateable by anyone
-
can(:update, Endpoints::SampleManifests::Instance)
-
can(:create, Endpoints::Studies::Instance::SampleManifests::CreateForPlates)
-
can(:create, Endpoints::Studies::Instance::SampleManifests::CreateForTubes)
-
can(:create, Endpoints::Studies::Instance::SampleManifests::CreateForMultiplexedLibraries)
-
end
-
-
def registered?
-
false
-
end
-
private :registered?
-
-
# Updates the abilities of the user based on the currently authenticated user instance. If the user
-
# unauthenticated then the API remains read-only.
-
def authenticated!
-
abilitise(:authenticated) if @request.user.present?
-
end
-
end
-
-
class Application < Base
-
-
recorder_helper(:tag_plates)
-
-
1
def initialize(request)
-
@api_application = ApiApplication.find_by_key(request.authorisation_code)
-
super
-
end
-
-
1
def privilege
-
@api_application.privilege.to_sym
-
end
-
-
1
unregistered do
-
# The API is designed to be read-only, at least.
-
can(:read, :all)
-
-
# Every application is entitled to be able to lookup UUIDs and make searches
-
can(:create, [ Endpoints::Uuids::Model::Lookup, Endpoints::Uuids::Model::Bulk ])
-
can(:create, [ Endpoints::Searches::Instance::First, Endpoints::Searches::Instance::All, Endpoints::Searches::Instance::Last ])
-
end
-
-
# Registered applications can manage all objects that allow it and can have unauthenicated users.
-
1
full do
-
can(:manage, :all)
-
can(:authenticate, :all)
-
end
-
-
# State changes only
-
1
tag_plates do
-
can(:create, [ Endpoints::StateChanges::Model ])
-
can(:authenticate, :all)
-
end
-
-
1
def registered?
-
@api_application.present?
-
end
-
1
private :registered?
-
-
# The decision as to whether the application requires the user to be authenticated is made
-
# by the application. If it does, however, then the user abilities may need to be changed
-
# so we need to modify that too.
-
1
def authenticate!(user_ability)
-
single_sign_on_cookie = @request.authentication_code
-
if single_sign_on_cookie.blank? and cannot?(:authenticate, :nil)
-
Core::Service::Authentication::UnauthenticatedError.no_cookie!
-
elsif not single_sign_on_cookie.blank?
-
user = ::User.find_by_api_key(single_sign_on_cookie) or Core::Service::Authentication::UnauthenticatedError.unauthenticated!
-
@request.service.instance_variable_set(:@user, user)
-
end
-
-
user_ability.authenticated!
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Core::Benchmarking
-
1
def self.registered(app)
-
1
app.helpers self
-
end
-
-
1
def benchmark(message = nil, &block)
-
yield
-
#ActiveRecord::Base.benchmark("===== API benchmark (#{message || 'general'}):", Logger::ERROR, &block)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Core::Endpoint::Base
-
1
module InstanceBehaviour
-
1
class Handler < Core::Endpoint::BasicHandler
-
1
def _read(request, _)
-
yield(self, request.target)
-
end
-
1
private :_read
-
1
standard_action(:read)
-
end
-
-
1
def self.extended(base)
-
1
base.class_attribute :instance_handler, :instance_writer => false
-
end
-
-
1
def instance(&block)
-
110
handler = Class.new(Handler).tap { |handler| const_set(:Instance, handler) }.new(&block)
-
55
handler.instance_variable_set(:@name, name)
-
55
self.instance_handler = handler
-
end
-
end
-
-
1
module ModelBehaviour
-
1
class Handler < Core::Endpoint::BasicHandler
-
1
include Core::Endpoint::BasicHandler::Paged
-
-
1
def _read(request, _)
-
request.target.send(:with_scope, :find => { :order => 'id ASC' }) do
-
page = request.path.first.try(:to_i) || 1
-
results = page_of_results(request.io.eager_loading_for(request.target).include_uuid, page, request.target)
-
results.singleton_class.send(:define_method, :model) { request.target }
-
yield(self, results)
-
end
-
end
-
1
private :_read
-
1
standard_action(:read)
-
end
-
-
1
def self.extended(base)
-
1
base.class_attribute :model_handler, :instance_writer => false
-
end
-
-
1
def model(&block)
-
106
handler = Class.new(Handler).tap { |handler| const_set(:Model, handler) }.new(&block)
-
53
self.model_handler = handler
-
end
-
end
-
-
1
extend InstanceBehaviour
-
1
extend ModelBehaviour
-
-
1
def self.root
-
60
self.name.sub(/^(::)?Endpoints::/, '').underscore.pluralize
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Core::Endpoint::BasicHandler
-
1
module Json
-
1
def actions(object, options)
-
Hash[@actions.select do |name, behaviour|
-
accessible_action?(self, behaviour, options[:response].request, object)
-
end.map do |name, behaviour|
-
[name, core_path(options)]
-
end]
-
end
-
1
private :actions
-
-
1
def root_json
-
'unknown'
-
end
-
-
1
def related
-
[]
-
end
-
-
1
def tree_for(object, options)
-
associations, actions = {}, {}
-
related.each { |r| r.separate(associations, actions) }
-
Core::Io::Json::Grammar::Root.new(
-
root_json,
-
associations.merge('actions' => Core::Io::Json::Grammar::Actions.new(self, actions))
-
)
-
end
-
-
1
def core_path(*args)
-
options = args.extract_options!
-
response = options[:response]
-
-
root =
-
if options[:target].respond_to?(:uuid)
-
options[:target].uuid
-
elsif not options[:endpoint].nil?
-
options[:endpoint].root
-
elsif not response.request.endpoint.nil?
-
response.request.endpoint.root
-
end
-
args.unshift(root) unless root.nil?
-
-
options[:response].request.service.api_path(*args)
-
end
-
1
private :core_path
-
-
1
def attach_action(name, behaviour = name)
-
47
@actions[name.to_sym] = behaviour.to_sym
-
end
-
1
private :attach_action
-
end
-
-
1
extend Core::Endpoint::BasicHandler::Actions::Standard
-
-
1
def initialize(&block)
-
162
@actions = self.class.standard_actions.dup
-
162
super
-
162
instance_eval(&block) if block_given?
-
end
-
-
1
include Core::Endpoint::BasicHandler::Json
-
1
include Core::Endpoint::BasicHandler::Actions
-
1
include Core::Endpoint::BasicHandler::Handlers
-
1
include Core::Endpoint::BasicHandler::Associations::HasMany
-
1
include Core::Endpoint::BasicHandler::Associations::BelongsTo
-
1
include Core::Endpoint::BasicHandler::Associations::HasFile
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
module Core::Endpoint::BasicHandler::Actions
-
class UnsupportedAction < StandardError
-
def initialize(action, request)
-
super(action.to_s)
-
end
-
end
-
-
def self.included(base)
-
base.class_eval do
-
include Core::Endpoint::BasicHandler::Actions::Bound
-
include Core::Endpoint::BasicHandler::Actions::Factory
-
include Core::Endpoint::BasicHandler::Actions::Guards
-
include Core::Endpoint::BasicHandler::EndpointLookup
-
include Core::Abilities::ActionBehaviour
-
end
-
end
-
-
ACTIONS_WITH_SUCCESS_CODES = {
-
:create => 201,
-
:read => 200,
-
:update => 200,
-
:delete => 200,
-
-
:create_from_file => 201,
-
:update_from_file => 200
-
}
-
-
ACTIONS_WITH_SUCCESS_CODES.each do |action, status_code|
-
line = __LINE__ + 1
-
class_eval(%Q{
-
def #{action}(request, path, &block)
-
current, *rest = path
-
handler = handler_for(current)
-
return handler.#{action}(request, rest, &block) unless self == handler
-
-
check_request_io_class!(request)
-
check_authorisation!(self, #{action.inspect}, request, request.target)
-
request.response do |response|
-
response.status(#{status_code})
-
_#{action}(request, response) do |handler, object|
-
response.handled_by ||= handler
-
response.object = object
-
end
-
end
-
end
-
-
def _#{action}(request, response)
-
raise ::Core::Service::UnsupportedAction
-
end
-
}, __FILE__, line)
-
end
-
-
def check_request_io_class!(request)
-
raise StandardError, "Need an I/O class for this request" if request.io.nil?
-
end
-
-
def does_not_require_an_io_class
-
1
self.singleton_class.class_eval(%Q{def check_request_io_class!(_) ; end}, __FILE__, __LINE__)
-
end
-
-
def disable(*actions)
-
actions.each do |action|
-
line = __LINE__ + 1
-
singleton_class.class_eval(%Q{
-
def _#{action}(request, response)
-
raise ::Core::Service::UnsupportedAction
-
end
-
}, __FILE__, line)
-
@actions.delete(action.to_sym)
-
end
-
end
-
-
def action(name, options = {}, &block)
-
declare_action(name, options, &block)
-
attach_action(options[:as] || name, name)
-
action_guard(name, options[:if]) if options.key?(:if)
-
end
-
-
def declare_action(name, options, &block)
-
action_implementation_method =
-
case
-
when block_given?
-
singleton_class.class_eval { define_method(:"_#{name}_internal", &block) }
-
:"_#{name}_internal"
-
-
when options[:to] then options[:to]
-
else raise StandardError, "Block or :to option needed to declare action"
-
end
-
-
line = __LINE__ + 1
-
singleton_class.class_eval(%Q{
-
def _#{name}(request, response)
-
object = #{action_implementation_method}(request, response)
-
yield(endpoint_for_object(object).instance_handler, object)
-
end
-
}, __FILE__, line)
-
end
-
private :declare_action
-
-
def generate_json_actions(object, options)
-
options[:stream].block('actions') do |result|
-
actions(object, options).each do |name, url|
-
result.attribute(name, url)
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::Actions::Bound
-
1
def bind_action(name, options, &block)
-
22
class_handler = Class.new(Handler).tap { |handler| self.class.const_set(options[:as].to_s.camelize, handler) }
-
11
register_handler(options[:to], class_handler.new(self, name, options, &block))
-
end
-
-
1
def self.delegate_to_bound_handler(name, target = name)
-
2
line = __LINE__ + 1
-
2
class_eval(%Q{
-
2
def bound_#{name}(name, *args, &block)
-
2
_handler_for(name).#{target}(*args, &block)
-
end
-
})
-
end
-
-
1
delegate_to_bound_handler :action_guard
-
1
delegate_to_bound_handler :action_does_not_require_an_io_class, :does_not_require_an_io_class
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
# An instance of this class is responsible for dealing with a particular named action on a
-
# resource. It is as though this instance is actually part of the instance that it was
-
# registered within.
-
1
class Core::Endpoint::BasicHandler::Actions::Bound::Handler < Core::Endpoint::BasicHandler
-
1
include Core::Endpoint::BasicHandler::Actions::InnerAction
-
-
1
def initialize(owner, name, options, &block)
-
11
super(name, options, &block)
-
11
@owner = owner
-
end
-
-
1
def owner_for(request, object)
-
endpoint_for_object(object).instance_handler
-
rescue => exception
-
@owner
-
end
-
1
private :owner_for
-
-
1
def core_path(*args)
-
args.unshift(@options[:to])
-
@owner.send(:core_path, *args)
-
end
-
1
private :core_path
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::Actions::Factory
-
1
class Nested < Core::Endpoint::BasicHandler
-
1
def initialize(name, &block)
-
1
super(&block)
-
1
@name = name.to_s
-
end
-
-
1
def separate(associations, _)
-
associations[@name] = lambda do |object, options, stream|
-
stream.block(@name) do |nested_stream|
-
nested_stream.block('actions') do |action_stream|
-
actions(object, options.merge(:target => object)).map do |action,url|
-
action_stream.attribute(action,url)
-
end
-
end
-
end
-
end
-
end
-
-
1
def core_path(*args)
-
super(@name, *args)
-
end
-
end
-
-
1
def nested(json, &block)
-
2
class_handler = Class.new(Nested).tap { |handler| self.class.const_set(json.to_s.camelize, handler) }
-
1
register_handler(json, class_handler.new(json, &block))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
module ::Core::Endpoint::BasicHandler::Actions::Guards
-
class Guard
-
def initialize(method = nil, &block)
-
if method.present?
-
line = __LINE__ + 1
-
singleton_class.class_eval(%Q{
-
1
def execute(object)
-
object.#{method}
-
end
-
}, __FILE__, line)
-
elsif block_given?
-
singleton_class.send(:define_method, :execute, &block)
-
else
-
raise StandardError, 'Either method name or block is required for guards'
-
end
-
end
-
end
-
-
class GuardChain
-
def initialize
-
@guards = []
-
end
-
-
1
delegate :push, :to => :@guards
-
-
def execute(object)
-
return true if @guards.empty?
-
@guards.all? { |guard| guard.execute(object) }
-
end
-
end
-
-
class GuardProxy < ActiveSupport::BasicObject
-
def initialize(request, object)
-
@request, @object = request, object
-
end
-
-
def respond_to?(method, private_methods = false)
-
super || @object.respond_to?(method, private_methods)
-
end
-
-
def method_missing(name, *args, &block)
-
@object.send(name, *args, &block)
-
end
-
protected :method_missing
-
end
-
-
def check_authorisation!(*args)
-
accessible_action?(*args) or
-
raise ::Core::Service::UnsupportedAction, 'requested action is not supported on this resource'
-
end
-
private :check_authorisation!
-
-
def accessible_action?(handler, action, request, object)
-
guard_for(action).execute(GuardProxy.new(request, object))
-
end
-
private :accessible_action?
-
-
def action_guard(name, method_name = nil, &block)
-
guard_for(name).push(Guard.new(method_name, &block))
-
end
-
-
def guard_for(name)
-
@guards ||= Hash.new { |h,k| h[k] = GuardChain.new }
-
@guards[name.to_sym]
-
end
-
private :guard_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
module Core::Endpoint::BasicHandler::Actions::InnerAction
-
def initialize(name, options, &block)
-
raise StandardError, "Cannot declare inner action #{name.inspect} without a block" unless block_given?
-
-
super() { }
-
@options, @handler = options, block
-
action(name, options)
-
end
-
-
def separate(_, actions)
-
actions[@options[:to].to_s] = lambda do |object, options, stream|
-
actions(object, options.merge(:target => object)).map do |action,url|
-
stream.attribute(action,url)
-
end
-
end
-
end
-
-
def for_json
-
nil
-
end
-
-
def rooted_json(options, &block)
-
return yield(options[:stream]) if @options.key?(:json)
-
options[:stream].block(@options[:json].to_s, &block)
-
end
-
private :rooted_json
-
-
def generate_json_actions(object, options)
-
rooted_json(options) do |stream|
-
super(object, options.merge(:stream => stream))
-
end
-
end
-
-
def declare_action(name, options)
-
line = __LINE__ + 1
-
singleton_class.class_eval(%Q{
-
1
def _#{name}(request, response)
-
object = @handler.call(self, request, response)
-
yield(owner_for(request, object), object)
-
end
-
}, __FILE__, line)
-
end
-
private :declare_action
-
-
def core_path(*args)
-
super(@options[:to], *args)
-
end
-
private :core_path
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::Actions::Standard
-
1
def self.extended(base)
-
1
base.class_eval do
-
1
include InstanceMethods
-
-
1
class_attribute :standard_actions, :instance_writer => false
-
1
self.standard_actions = {}
-
end
-
end
-
-
1
def standard_action(*names)
-
3
self.standard_actions = {} if self.standard_actions.empty?
-
6
self.standard_actions.merge!(Hash[names.map { |a| [a.to_sym, a.to_sym] }])
-
end
-
-
1
module InstanceMethods
-
1
def standard_update!(request, _)
-
request.update!
-
end
-
1
private :standard_update!
-
-
1
def standard_create!(request, _)
-
request.create!
-
end
-
1
private :standard_create!
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
module Core::Endpoint::BasicHandler::Associations::BelongsTo
-
class Handler
-
include Core::Endpoint::BasicHandler::EndpointLookup
-
-
def initialize(name, options, &block)
-
57
@name, @options = name, options
-
57
@throughs = Array(options[:through])
-
end
-
-
def endpoint_details(object, &block)
-
object = @throughs.inject(object) { |t,s| t.send(s) }.send(@name) || return
-
block.call(@options[:json].to_s, endpoint_for_object(object), object)
-
end
-
private :endpoint_details
-
-
class Association
-
include Core::Io::Json::Grammar::Intermediate
-
include Core::Io::Json::Grammar::Resource
-
-
def initialize(endpoint_helper, children = nil)
-
super(children)
-
@endpoint_helper = endpoint_helper
-
end
-
1
-
delegate :endpoint_details, :to => :@endpoint
-
-
1
def merge(node)
-
super(node) { |children| self.class.new(@endpoint_helper, children) }
-
end
-
-
1
def call(object, options, stream)
-
@endpoint_helper.call(object) do |json_root, endpoint, associated_object|
-
stream.block(json_root) do |nested_stream|
-
resource_details(endpoint.instance_handler, associated_object, options, stream)
-
super(object, options, nested_stream)
-
end
-
end
-
end
-
-
1
def actions(*args)
-
# Nothing needed here!
-
end
-
end
-
-
1
def separate(associations, _)
-
associations[@options[:json].to_s] = Association.new(method(:endpoint_details))
-
end
-
end
-
-
1
def initialize
-
162
super
-
162
@endpoints = []
-
end
-
-
1
def belongs_to(name, options, &block)
-
114
class_handler = Class.new(Handler).tap { |handler| self.class.const_set(name.to_s.camelize, handler) }
-
57
@endpoints.push(class_handler.new(name, options, &block))
-
end
-
-
1
def related
-
super.concat(@endpoints)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::Associations::HasFile
-
1
def has_file(options)
-
1
::Api::EndpointHandler.register_mimetype(options[:content_type])
-
1
@supported_types ||= {}
-
1
@supported_types[options[:content_type]]= options[:as]||:retrieve_file
-
end
-
-
1
def content_type(content_type)
-
return nil unless @supported_types.present?
-
@supported_types[content_type]
-
end
-
-
1
def file_through(content_types)
-
content_type(content_types.detect do |ct|
-
content_type(ct)
-
end)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::Associations::HasMany
-
1
def has_many(name, options, &block)
-
84
class_handler = Class.new(Handler).tap { |handler| self.class.const_set(name.to_s.camelize, handler) }
-
42
register_handler(options[:to], class_handler.new(name, options, &block))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
class Core::Endpoint::BasicHandler::Associations::HasMany::Handler < Core::Endpoint::BasicHandler
-
include Core::Endpoint::BasicHandler::Paged
-
-
def initialize(association, options, &block)
-
42
super(&block)
-
42
@association, @options = association, options
-
end
-
-
[ :create, :update, :delete ].each do |action|
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{action}(request, path)
-
nested_action(request, path, request.target.send(@association)) do
-
super
-
end
-
end
-
}, __FILE__, line)
-
end
-
-
1
def association_details_for(request)
-
association_class = request.target.class.reflections[@association].klass
-
association_io = ::Core::Io::Registry.instance.lookup_for_class(association_class)
-
yield(association_io)
-
end
-
1
private :association_details_for
-
-
1
def association_from(request)
-
association = request.target.send(@association)
-
association = @options[:scoped].split('.').inject(association) { |c,m| c.send(m) } if @options.key?(:scoped)
-
association
-
end
-
1
private :association_from
-
-
1
def nested_action(request, path, association, &block)
-
uuid = request.target.uuid
-
association_details_for(request) do |association_io|
-
request.io = association_io
-
request.push(association) do
-
association.singleton_class.send(:define_method, :uuid) { uuid } unless association.respond_to?(:uuid)
-
yield
-
end
-
end
-
end
-
1
private :nested_action
-
-
1
def read(request, path)
-
association_details_for(request) do |association_io|
-
association = association_from(request)
-
eager_loaded = association_io.eager_loading_for(association).include_uuid
-
nested_action(request, path, page_of_results(eager_loaded, path.first.try(:to_i) || 1, association)) do
-
super
-
end
-
end
-
end
-
-
1
def _read(request, _)
-
yield(self, request.target)
-
end
-
1
private :_read
-
1
standard_action(:read)
-
-
1
def separate(associations, _)
-
associations[@options[:json].to_s] = lambda do |object, options, stream|
-
stream.block(@options[:json].to_s) do |nested_stream|
-
association = object.send(@association)
-
nested_stream.attribute('size', association.count)
-
-
nested_stream.block('actions') do |action_stream|
-
actions(
-
count_of_pages(association),
-
options.merge(:target => object)
-
).map do |action,url|
-
action_stream.attribute(action,url)
-
end
-
end
-
end
-
end
-
end
-
-
1
def core_path(*args)
-
options = args.extract_options!
-
options[:response].request.service.api_path(options[:target].uuid, @options[:to], *args)
-
end
-
1
private :core_path
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::EndpointLookup
-
1
EndpointError = Class.new(StandardError)
-
1
MissingEndpoint = Class.new(EndpointError)
-
-
-
1
def endpoint_for(model, root = model)
-
raise EndpointError, "Incorrect hierarchy for #{root.inspect}" if model.nil?
-
raise MissingEndpoint, "No endpoint for the model #{root.inspect}" if model == ActiveRecord::Base
-
-
endpoint_name = [ 'Endpoints', model.name.pluralize ].join('::')
-
-
begin
-
endpoint_name.constantize
-
rescue NameError => exception
-
# Some performance improvement can be made by storing the class that is found for those
-
# that are missing. Meaning the next time we shouldn't be coming through this path.
-
cache_endpoint_as(endpoint_name, endpoint_for(model.superclass, root))
-
end
-
end
-
1
private :endpoint_for
-
-
1
def cache_endpoint_as(endpoint_name, endpoint)
-
root, *rest = endpoint_name.split('::')
-
leaf = rest.pop
-
module_parent = rest.inject(root.constantize, &method(:constant_lookup))
-
constant_lookup(module_parent, leaf, endpoint)
-
end
-
1
private :cache_endpoint_as
-
-
1
def constant_lookup(current, module_name, value = nil)
-
# NOTE: Do not use const_get and rescue NameError here because that causes Rails to load the model
-
return current.const_get(module_name) if current.const_defined?(module_name,false)
-
current.const_set(module_name, value || Module.new)
-
end
-
1
private :constant_lookup
-
-
1
def endpoint_for_object(model_instance)
-
endpoint_for(model_instance.class)
-
end
-
1
private :endpoint_for_object
-
-
1
def endpoint_for_class(model_class)
-
endpoint_for(model_class)
-
end
-
1
private :endpoint_for_class
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Core::Endpoint::BasicHandler::Handlers
-
# Handler that behaves like it never deals with any URLs
-
1
NullHandler = Object.new.tap do |handler|
-
1
[ :create, :read, :update, :delete ].each do |action|
-
4
eval(%Q{
-
4
def handler.#{action}(*args, &block)
-
raise ::Core::Service::UnsupportedAction
-
end
-
})
-
end
-
end
-
-
1
def initialize
-
162
super
-
162
@handlers = {}
-
end
-
-
1
def related
-
@handlers.map(&:last)
-
end
-
-
1
def actions(object, options)
-
@handlers.select do |name, handler|
-
handler.is_a?(Core::Endpoint::BasicHandler::Actions::InnerAction)
-
# accessible_action?(self, handler, options[:response].request, object)
-
end.map do |name, handler|
-
handler.send(:actions, object, options)
-
end.inject(super) do |actions, subactions|
-
actions.merge(subactions)
-
end
-
end
-
-
1
def register_handler(segment, handler)
-
54
@handlers[segment.to_sym] = handler
-
end
-
1
private :register_handler
-
-
1
def handler_for(segment)
-
return self if segment.nil?
-
_handler_for(segment) || NullHandler
-
end
-
1
private :handler_for
-
-
1
def _handler_for(segment)
-
2
@handlers[segment.to_sym]
-
end
-
1
private :_handler_for
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
module Core::Endpoint::BasicHandler::Paged
-
def self.page_accessor(action, will_paginate_method, default_value = nil)
-
lambda do |object|
-
page = object.send(will_paginate_method) || default_value
-
page.nil? ? nil : [action, [1,page].max]
-
end
-
end
-
-
ACTION_NAME_TO_PAGE_METHOD = [
-
page_accessor(:last, :total_pages, 1),
-
page_accessor(:previous, :previous_page),
-
page_accessor(:next, :next_page),
-
page_accessor(:read, :current_page, 1)
-
]
-
-
def actions(object, options)
-
super.tap do |actions|
-
actions.merge!(pages_to_actions(object, options)) if options[:handled_by] == self
-
end
-
end
-
private :actions
-
-
def action_updates_for(options)
-
response = options[:response]
-
return unless response.handled_by == self
-
yield(pages_to_actions(response.object, options))
-
end
-
private :action_updates_for
-
-
def pages_to_actions(object, options)
-
actions_to_details = [[:first,1]] + ACTION_NAME_TO_PAGE_METHOD.map { |c| c.call(object) }.compact
-
Hash[actions_to_details.map { |action,page| [action,core_path(page, options)] }]
-
end
-
private :pages_to_actions
-
-
def handler_for(segment)
-
(segment.to_s =~ /^\d+$/) ? self : super
-
end
-
private :handler_for
-
-
def page_of_results(target, page = 1, model = target)
-
raise ActiveRecord::RecordNotFound, 'before the start of the results' if page <= 0
-
target.paginate(
-
:page => page,
-
:per_page => Core::Endpoint::BasicHandler::Paged.results_per_page,
-
:total_entries => model.count
-
).tap do |results|
-
raise ActiveRecord::RecordNotFound, 'past the end of the results' if (page > 1) && (page > results.total_pages)
-
end
-
end
-
private :page_of_results
-
-
class PagedTarget
-
def initialize(model)
-
@model = model
-
end
-
1
-
delegate :count, :to => :@model
-
-
1
class PageOfResults
-
1
def initialize(page, total, per_page)
-
@page, @total_pages = page, page / per_page
-
end
-
-
1
attr_reader :page, :total_pages
-
1
alias_method(:current_page, :page)
-
-
1
def next_page
-
page + 1
-
end
-
-
1
def previous_page
-
page - 1
-
end
-
end
-
-
1
def paginate(options)
-
PageOfResults.new(options[:page], options[:total_entries], options[:per_page])
-
end
-
end
-
-
1
def count_of_pages(target, page = 1)
-
page_of_results(PagedTarget.new(target), page)
-
end
-
1
private :count_of_pages
-
-
# For a convenience allow people to override the number of results that are returned per page. This is
-
# really only used in the Cucumber features where we want to see more or less than the defaults.
-
1
mattr_accessor :results_per_page
-
1
self.results_per_page = 100
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
module Core::Initializable
-
class Initializer
-
def initialize(owner)
-
@owner = owner
-
end
-
-
class << self
-
def delegated_attribute_writer(*names)
-
names.each do |name|
-
class_eval(%Q{def #{name}=(value) ; @owner.instance_variable_set(:@#{name}, value) ; end})
-
end
-
delegate_to_owner(*names)
-
end
-
-
def delegate_to_owner(*names)
-
1
names.push(:to => :@owner)
-
delegate(*names)
-
end
-
end
-
end
-
-
def self.extended(base)
-
base.class_eval do
-
include InstanceMethods
-
const_set(:Initializer, Class.new(Core::Initializable::Initializer))
-
end
-
end
-
-
def initialized_attr_reader(*names)
-
attr_reader(*names)
-
self::Initializer.delegated_attribute_writer(*names)
-
end
-
-
def initialized_delegate(*names)
-
self::Initializer.delegate_to_owner(*names)
-
end
-
-
module InstanceMethods
-
def initialize(&block)
-
yield(self.class::Initializer.new(self)) if block_given?
-
end
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Core::Io::Base
-
1
extend ::Core::Logging
-
1
extend ::Core::Benchmarking
-
1
extend ::Core::Io::Base::EagerLoadingBehaviour
-
1
extend ::Core::Io::Base::JsonFormattingBehaviour
-
-
1
class << self
-
1
def map_parameters_to_attributes(*args)
-
{}
-
end
-
1
private :map_parameters_to_attributes
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Core::Io::Base::EagerLoadingBehaviour
-
1
def set_eager_loading(&block)
-
34
singleton_class.class_eval do
-
34
define_method(:eager_loading_for) do |loaded_class|
-
block.call(super(loaded_class))
-
end
-
end
-
end
-
-
1
def eager_loading_for(model)
-
model or raise StandardError, "nil model does not make sense here at all!"
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
module Core::Io::Base::JsonFormattingBehaviour
-
def self.extended(base)
-
base.class_eval do
-
extend ::Core::Io::Base::JsonFormattingBehaviour::Input
-
extend ::Core::Io::Base::JsonFormattingBehaviour::Output
-
-
class_attribute :attribute_to_json_field, :instance_writer => false
-
1
self.attribute_to_json_field = {}
-
delegate :json_field_for, :to => 'self.class'
-
end
-
end
-
-
# NOTE: This one is OK!
-
def as_json(options = nil, &block)
-
options ||= {}
-
object = options.delete(:object)
-
object_json(object, options)
-
end
-
-
#--
-
# Very root level does absolutely nothing useful!
-
#++
-
def object_json(*args)
-
-
end
-
-
def json_field_for(attribute)
-
return attribute_to_json_field[attribute.to_s] if attribute_to_json_field.key?(attribute.to_s)
-
-
# We have to assume that this could be an association that is being exposed, in which case we'll
-
# need to determine the I/O class that deals with it and hand off the error handling to it.
-
association, *association_parts = attribute.to_s.split('.')
-
return attribute.to_s if association_parts.empty?
-
reflection = model_for_input.reflections[association.to_sym]
-
return attribute.to_s if reflection.nil?
-
-
# TODO: 'association' here should really be garnered from the appropriate endpoint
-
association_json_field = ::Core::Io::Registry.instance.lookup_for_class(reflection.klass).json_field_for(association_parts.join('.'))
-
"#{association}.#{association_json_field}"
-
end
-
-
def set_json_root(name)
-
73
@json_root = name.to_sym
-
end
-
-
def json_root
-
@json_root or raise StandardError, "JSON root is not set for #{self.name}"
-
end
-
-
def api_root
-
self.json_root.to_s.pluralize
-
end
-
-
def define_attribute_and_json_mapping(mapping)
-
73
parse_mapping_rules(mapping) do |attribute_to_json, json_to_attribute|
-
73
self.attribute_to_json_field.merge!(Hash[attribute_to_json])
-
73
generate_object_to_json_mapping(attribute_to_json)
-
73
generate_json_to_object_mapping(json_to_attribute)
-
end
-
end
-
-
VALID_LINE_REGEXP = /^\s*((?:[a-z_][\w_]*\.)*[a-z_][\w_]*[?!]?)\s*(<=|<=>|=>)\s*((?:[a-z_][\w_]*\.)*[a-z_][\w_]*)\s*$/
-
-
def parse_mapping_rules(mapping, &block)
-
73
attribute_to_json, json_to_attribute = [], []
-
73
StringIO.new(mapping).each_line do |line|
-
552
next if line.blank? or line =~ /^\s*#/
-
365
match = VALID_LINE_REGEXP.match(line) or raise StandardError, "Invalid line: #{line.inspect}"
-
365
attribute_to_json.push([ match[1], match[3] ]) if (match[2] =~ /<?=>/)
-
365
json_to_attribute.push([ match[3], (match[2] =~ /<=>?/) ? match[1] : nil ])
-
end
-
73
yield(attribute_to_json, json_to_attribute)
-
end
-
private :parse_mapping_rules
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
module ::Core::Io::Base::JsonFormattingBehaviour::Input
-
class ReadOnlyAttribute < ::Core::Service::Error
-
def initialize(attribute)
-
super('is read-only')
-
@attribute = attribute
-
end
-
-
def api_error(response)
-
response.content_error(422, { @attribute => [ self.message ] })
-
end
-
end
-
-
def self.extended(base)
-
base.class_eval do
-
class_attribute :model_for_input, :instance_writer => false
-
extend AssociationHandling
-
end
-
end
-
-
def set_model_for_input(model)
-
self.model_for_input = model
-
end
-
-
def generate_json_to_object_mapping(json_to_attribute)
-
code = []
-
-
# Split the mappings into two to make things easier. Read only attributes are easily
-
# handled right now, provided there is not a read_write one that shares their name.
-
read_only, read_write = json_to_attribute.partition { |_, v| v.nil? }
-
common_keys = read_only.map(&:first) & read_write.map(&:first)
-
read_only.delete_if { |k,_| common_keys.include?(k) }
-
code.concat(read_only.map do |json, _|
-
%Q{process_if_present(params, #{json.split('.').inspect}) { |_| raise ReadOnlyAttribute, #{json.inspect} }}
-
end)
-
-
# Now the harder bit: for attribute we need to work out how we would fill in the attribute
-
# structure for an update_attributes! call.
-
initial_structure = {}
-
read_write.each do |json, attribute|
-
steps = attribute.split('.').map(&:to_sym)
-
trunk, leaf = steps[0..-2], steps.last
-
-
# This bit ends up with the 'path' for the inner bit of the attribute (i.e. if the attribute
-
# was 'a.b.c.d' then the inner bit is 'a.b.c' and this path could be 'a_attributes,
-
# b_attributes, c_attributes') and the final model, or rather association, that we end at.
-
# 'model' is nil if there is no association and we're assuming that we need a Hash of
-
# some form.
-
model, path = trunk.inject([ model_for_input, [] ]) do |(model, parts), step|
-
next_model, next_step =
-
if model.nil?
-
[ nil, step ]
-
elsif association = model.reflections[step]
-
raise StandardError, "Nested attributes only works with belongs_to or has_one" unless [ :belongs_to, :has_one ].include?(association.macro.to_sym)
-
[ association.klass, :"#{step}_attributes" ]
-
else
-
[ nil, step ]
-
end
-
-
[ next_model, parts << next_step ]
-
end
-
-
# Build the necessary structure for the attributes. The code can also be generated
-
# based on the information we have generated. If we ended at an association and the
-
# leaf is also an association then we have to change the behaviour based on the incoming
-
# JSON.
-
path.inject(initial_structure) { |part, step| part[step] ||= {} }
-
code << "process_if_present(params, #{json.split('.').inspect}) do |value|"
-
if path.empty?
-
code << " attributes.tap do |section|"
-
else
-
code << " #{path.inspect}.inject(attributes) { |a,s| a[s] }.tap do |section|"
-
end
-
-
if model.nil?
-
code << " section[#{leaf.inspect}] = value #nil"
-
elsif model.respond_to?(:reflections) and association = model.reflections[leaf]
-
code << " handle_#{association.macro}(section, #{leaf.inspect}, value, object)"
-
elsif model.respond_to?(:klass) and association = model.klass.reflections[leaf]
-
code << " handle_#{association.macro}(section, #{leaf.inspect}, value, object)"
-
else
-
code << " section[#{leaf.inspect}] = value"
-
end
-
code << " end"
-
code << "end"
-
end
-
-
low_level(('-' * 30) << self.name << ('-' * 30))
-
code.map(&method(:low_level))
-
low_level(('=' * 30) << self.name << ('=' * 30))
-
-
# Generate the code that the instance will actually use ...
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def self.map_parameters_to_attributes(params, object = nil, nested_in_another_model = false)
-
#{initial_structure.inspect}.tap do |attributes|
-
attributes.deep_merge!(super)
-
params = params.fetch(json_root.to_s, {}) unless nested_in_another_model
-
#{code.join("\n")}
-
end
-
end
-
}, __FILE__, line)
-
end
-
private :generate_json_to_object_mapping
-
-
# If the specified path is present all of the way to the end then the value at the
-
# leaf is yielded, otherwise this method simply returns.
-
def process_if_present(json, path)
-
value = path.inject(json) do |current,step|
-
return unless current.respond_to?(:key?) # Could be nested attribute but not present!
-
return unless current.key?(step)
-
current[step]
-
end
-
yield(value)
-
end
-
private :process_if_present
-
-
module AssociationHandling
-
def association_class(association, object)
-
object.try(association).try(:class) || model_for_input.reflections[association.to_sym].klass
-
end
-
private :association_class
-
-
def handle_belongs_to(attributes, attribute, json, object)
-
if json.is_a?(Hash)
-
uuid = json.delete('uuid')
-
associated = association_class(attribute, object)
-
if uuid.present?
-
attributes[attribute] = load_uuid_resource(uuid)
-
elsif associated.present?
-
io = ::Core::Io::Registry.instance.lookup_for_class(associated)
-
attributes[:"#{attribute}_attributes"] = io.map_parameters_to_attributes(json, nil, true)
-
else
-
# We really don't have any idea here so we're just going to take what's there as it!
-
attributes[:"#{attribute}_attributes"] = json
-
end
-
else
-
attributes[attribute] = load_uuid_resource(json)
-
end
-
end
-
private :handle_belongs_to
-
-
def load_uuid_resource(uuid)
-
record = Uuid.include_resource.lookup_single_uuid(uuid).resource
-
::Core::Io::Registry.instance.lookup_for_object(record).eager_loading_for(record.class).include_uuid.find(record.id)
-
end
-
private :load_uuid_resource
-
-
def handle_has_many(attributes, attribute, json, object)
-
if json.first.is_a?(Hash)
-
uuids = Uuid.include_resource.lookup_many_uuids(json.map { |j| j['uuid'] })
-
uuid_to_resource = Hash[uuids.map { |uuid| [uuid.external_id, uuid.resource] }]
-
mapped_attributes = json.map do |j|
-
uuid = j.delete('uuid') or raise StandardError, 'UUID missing from has_many update'
-
delete = j.delete('delete')
-
resource = uuid_to_resource[uuid]
-
io = ::Core::Io::Registry.instance.lookup_for_object(resource)
-
io.map_parameters_to_attributes(j, resource, true).tap do |mapped|
-
mapped[:id] = resource.id # UUID becomes ID
-
mapped[:delete] = delete unless delete.nil? # Are we deleting this one?
-
end
-
end
-
-
attributes[:"#{attribute}_attributes"] = mapped_attributes
-
else
-
attributes[attribute] = Uuid.include_resource.lookup_many_uuids(json).map(&:resource)
-
end
-
end
-
private :handle_has_many
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module ::Core::Io::Base::JsonFormattingBehaviour::Output
-
1
def json_code_tree
-
60
::Core::Io::Json::Grammar::Root.new(self)
-
end
-
1
private :json_code_tree
-
-
1
def generate_object_to_json_mapping(attribute_to_json)
-
# Sort the attribute_to_json map such that the JSON elements are in order, thus ensuring that
-
# we will only open and close blocks as we go. Then build a tree that can be executed against
-
# an object to generate the JSON appropriately.
-
73
tree = attribute_to_json.sort_by(&:last).map do |attribute, json|
-
337
[ json.split('.'), attribute.split('.').map(&:to_sym) ]
-
end.inject(json_code_tree.for(self)) do |tree, (json_path, attribute_path)|
-
337
tree.tap do
-
337
json_leaf = json_path.pop
-
503
json_path.inject(tree) { |node,step| node[step] }.leaf(json_leaf, attribute_path)
-
end
-
end
-
-
# Now we can generate a method that will use that tree to encode an object to JSON.
-
86
self.singleton_class.send(:define_method, :json_code_tree) { tree }
-
73
self.singleton_class.send(:define_method, :object_json, &tree.method(:encode))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ::Core::Io::Buffer
-
1
def initialize(stream)
-
@stream, @buffer = stream, StringIO.new
-
return unless block_given?
-
-
yield(self)
-
force_flush
-
end
-
-
1
def write(value)
-
@buffer.write(value)
-
force_flush if @buffer.string.length > configatron.api.flush_response_at
-
end
-
-
1
def flush
-
# Ignore flush for the moment
-
end
-
-
1
def force_flush
-
@stream.call(@buffer.string)
-
@buffer.reopen
-
end
-
1
private :force_flush
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module ::Core::Io::Collection
-
1
def as_json(options = {})
-
results, base_stream = options[:object], options[:stream]
-
-
base_stream.attribute(:size, size_for(results))
-
base_stream.array(options[:response].io.json_root.to_s.pluralize, results) do |stream, object|
-
stream.open do
-
::Core::Io::Registry.instance.lookup_for_object(object).object_json(
-
object, options.merge(
-
:stream => stream,
-
:target => object,
-
:nested => true
-
)
-
)
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2015 Genome Research Ltd.
-
module ::Core::Io::Json::Grammar
-
module Intermediate
-
attr_reader :children
-
-
def initialize(children)
-
209
@children = children || {}
-
end
-
-
def leaf(name, attribute)
-
337
@children[name] ||= Leaf.new(name, attribute)
-
end
-
-
def [](name)
-
166
@children[name] ||= Node.new(name)
-
end
-
-
def call(object, options, stream)
-
process_children(object, options, stream)
-
end
-
-
def process_children(object, options, stream)
-
@children.each do |_, child|
-
child.call(object, options, stream)
-
end
-
end
-
private :process_children
-
-
def merge(node)
-
yield(node.merge_children_with(self))
-
end
-
-
def merge_children_with(node)
-
Hash[
-
(node.children.keys + @children.keys).uniq.map do |k|
-
cloned = case
-
when @children.key?(k) && node.children.key?(k) then node.children[k].merge(@children[k])
-
when @children.key?(k) then @children[k]
-
when node.children.key?(k) then node.children[k]
-
else raise "Odd, how did that happen?"
-
end
-
-
[k,cloned]
-
end
-
]
-
end
-
-
def inspect
-
@children.values.inspect
-
end
-
-
def duplicate(&block)
-
282
yield(Hash[@children.map { |k,v| [k,v.dup] }])
-
end
-
private :duplicate
-
end
-
-
class Root
-
include Intermediate
-
-
def initialize(owner, children = nil)
-
133
super(children)
-
133
@owner = owner
-
end
-
-
def for(owner)
-
146
duplicate { |children| self.class.new(owner, children) }
-
end
-
1
-
delegate :json_root, :to => :@owner
-
-
1
def encode(object, options)
-
object_encoder(object, options).call(object, options, options[:stream])
-
end
-
-
# To encode an individual object we use our JSON serialization tree, merged with the actions
-
# tree from the endpoint would handle this object.
-
1
def object_encoder(object, options)
-
return self unless object.respond_to?(:uuid)
-
-
instance_handler = options[:handled_by].send(:endpoint_for_object, object).instance_handler
-
instance_handler.tree_for(object, options).merge(self) do |children|
-
self.class.new(@owner, children)
-
end
-
rescue Core::Endpoint::BasicHandler::EndpointLookup::MissingEndpoint => exception
-
# There is no endpoint for this, even though it has a UUID!
-
self
-
end
-
1
private :object_encoder
-
-
1
def call(object, options, stream)
-
rooted_json(stream, options[:nested]) do |stream|
-
stream.attribute('created_at', object.created_at)
-
stream.attribute('updated_at', object.updated_at)
-
super(object, options, stream)
-
end
-
end
-
-
1
def rooted_json(stream, nested, &block)
-
return yield(stream) if nested
-
stream.block(json_root, &block)
-
end
-
1
private :rooted_json
-
-
1
def inspect
-
"Root<#{super}>"
-
end
-
end
-
-
1
class Node
-
1
include Intermediate
-
1
attr_reader :name
-
-
1
def initialize(name, children = nil)
-
76
super(children)
-
76
@name = name
-
end
-
-
1
def call(object, options, stream)
-
stream.block(@name) do |stream|
-
super(object, options, stream)
-
end
-
end
-
-
1
def dup
-
68
duplicate { |children| self.class.new(@name, children) }
-
end
-
-
1
def merge(node)
-
super(node) { |children| self.class.new(@name, children) }
-
end
-
-
1
def inspect
-
"Node<#{@name},#{super}>"
-
end
-
end
-
-
1
class Leaf
-
1
attr_reader :name
-
-
1
def initialize(name, attribute)
-
477
@name = name
-
477
@attribute = attribute.pop
-
477
@attribute_path = attribute
-
end
-
-
1
def call(object, options, stream)
-
value = @attribute_path.inject(object) { |o,k| return if o.nil? ; o.send(k) } or return
-
stream.attribute(@name, value.send(@attribute), options)
-
end
-
-
1
def dup
-
282
attribute = @attribute_path.dup.tap { |p| p << @attribute }
-
141
self.class.new(name, attribute)
-
end
-
-
1
def merge(node)
-
raise "Cannot merge into a leaf as it is attribute only!"
-
end
-
-
1
def merge_children_with(node)
-
key = "_#{name}"
-
raise "Cannot merge as existing leaf node '#{key}'" if node.children.key?(key)
-
node.children.merge(key => self)
-
end
-
-
1
def inspect
-
"Leaf<#{@name},#{@attribute},#{@attribute_path.inspect}>"
-
end
-
end
-
-
1
module Resource
-
1
def resource_details(endpoint, object, options, stream)
-
stream.block('actions') do |nested_stream|
-
endpoint.send(:actions, object, options.merge(:target => object)).map do |action,url|
-
nested_stream.attribute(action,url)
-
end
-
actions(object, options, nested_stream)
-
end
-
stream.attribute('uuid', object.uuid)
-
end
-
end
-
-
1
class Actions
-
1
include Intermediate
-
1
include Resource
-
-
1
def initialize(endpoint, children = nil)
-
super(children)
-
@endpoint = endpoint
-
end
-
-
1
def call(object, options, stream)
-
resource_details(@endpoint, object, options, stream)
-
end
-
-
1
def actions(object, options, stream)
-
process_children(object, options, stream)
-
end
-
-
1
def merge(_)
-
raise "Cannot merge into an actions leaf as it is actions only!"
-
end
-
-
1
def dup
-
self.class.new(@endpoint)
-
end
-
-
1
def inspect
-
"Actions<#{super}>"
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2014 Genome Research Ltd.
-
1
module ::Core::Io::Json
-
1
class Stream
-
1
def initialize(buffer)
-
@buffer, @have_output_value = buffer, []
-
end
-
-
1
def open(&block)
-
flush do
-
unencoded('{')
-
yield(self)
-
unencoded('}')
-
end
-
end
-
-
1
def array(attribute, objects, &block)
-
named(attribute) do
-
array_encode(objects) { |v| yield(self, v) }
-
end
-
end
-
-
1
def attribute(attribute, value, options = {})
-
named(attribute) do
-
encode(value, options)
-
end
-
end
-
-
1
def block(attribute, &block)
-
named(attribute) { open(&block) }
-
end
-
-
1
def encode(object, options = {})
-
case
-
when object.nil? then unencoded('null')
-
when Symbol === object then string_encode(object)
-
when TrueClass === object then unencoded('true')
-
when FalseClass === object then unencoded('false')
-
when String === object then string_encode(object)
-
when Fixnum === object then unencoded(object.to_s)
-
when Float === object then unencoded(object.to_s)
-
when Date === object then string_encode(object)
-
when ActiveSupport::TimeWithZone === object then string_encode(object.to_s)
-
when Time === object then string_encode(object.to_s(:compatible))
-
when Hash === object then hash_encode(object, options)
-
when object.respond_to?(:zip) then array_encode(object) { |o| encode(o, options) }
-
else object_encode(object, options)
-
end
-
end
-
-
1
def object_encode(object, options)
-
open do
-
::Core::Io::Registry.instance.lookup_for_object(object).object_json(
-
object, options.merge(
-
:stream => self,
-
:object => object,
-
:nested => true
-
)
-
)
-
end
-
end
-
1
private :object_encode
-
-
1
def named(attribute, &block)
-
unencoded(',') if have_output_value?
-
encode(attribute)
-
unencoded(':')
-
yield
-
ensure
-
have_output_value
-
end
-
-
1
def hash_encode(hash, options)
-
open do |stream|
-
hash.each do |k,v|
-
stream.attribute(k.to_s, v, options)
-
end
-
end
-
end
-
1
private :hash_encode
-
-
1
def array_encode(array, &block)
-
unencoded('[')
-
# Use length rather than size, as otherwise we perform
-
# a count query. Not only is this unnecessary, but seems
-
# to generate inaccurate numbers in some cases.
-
array.zip([',']*(array.length-1)).each do |value, separator|
-
yield(value)
-
unencoded(separator) unless separator.nil?
-
end unless array.empty?
-
unencoded(']')
-
end
-
1
private :array_encode
-
-
1
def string_encode(object)
-
unencoded(object.to_json)
-
end
-
1
private :string_encode
-
-
1
def unencoded(value)
-
@buffer.write(value)
-
end
-
1
private :unencoded
-
-
1
def flush(&block)
-
@have_output_value[0] = false
-
yield
-
@buffer.flush
-
ensure
-
@have_output_value.shift
-
end
-
1
private :flush
-
-
1
def have_output_value?
-
@have_output_value.first
-
end
-
1
private :have_output_value?
-
-
1
def have_output_value
-
@have_output_value[0] = true
-
end
-
1
private :have_output_value
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
class Core::Io::List
-
include Core::Benchmarking
-
-
def initialize(command, objects)
-
@command, @container = command, command.send(:container)
-
@objects = objects.map { |o| Core::Io::Registry.instance.lookup(o.class).create!(@container, o) }
-
@current_page, @last_page = [ 1, objects.current_page ].max, [ 1, objects.total_pages ].max
-
end
-
1
-
delegate :action_for_page, :to => :@command
-
-
1
def pagination_actions
-
{
-
:first => action_for_page(1),
-
:last => action_for_page(@last_page),
-
:read => action_for_page(@current_page)
-
}.tap do |actions_to_page|
-
actions_to_page[:previous] = action_for_page(@current_page-1) unless @current_page == 1
-
actions_to_page[:next] = action_for_page(@current_page+1) unless @current_page == @last_page
-
end
-
end
-
1
private :pagination_actions
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Core::Io::Registry < ::Core::Registry
-
# Looks up the I/O class by guessing at the name based on the model. If it finds it it then registers
-
# that class for the model class specified.
-
1
def lookup_target_class_in_registry(model_class)
-
in_current_registry = super
-
return in_current_registry unless in_current_registry.nil?
-
register(model_class, "::Io::#{model_class.name}".constantize)
-
rescue NameError => exception
-
nil
-
end
-
1
private :lookup_target_class_in_registry
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Core::Logging
-
1
def self.logging_helper(name)
-
3
module_eval <<-END_OF_HELPER
-
3
def #{name}(message)
-
3
Rails.logger.#{name}("API(\#{(self.is_a?(Class) ? self : self.class).name}): \#{message}")
-
end
-
END_OF_HELPER
-
end
-
-
1
[ :debug, :info, :error ].each do |level|
-
3
logging_helper(level)
-
end
-
-
1
def low_level(*args)
-
#debug(*args)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Core::References
-
# Discards all of the references that this object is keeping to other objects. This means that
-
# all of the objects should be garbage collected, rather than a proportion that are external to an
-
# instance of this class.
-
1
def discard_all_references
-
instance_variables.each { |name| instance_variable_set(name, nil) }
-
end
-
1
private :discard_all_references
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ::Core::Registry
-
1
include ::Singleton
-
1
include ::Core::Logging
-
-
1
RegistryError = Class.new(StandardError)
-
1
AlreadyRegisteredError = Class.new(RegistryError)
-
1
UnregisteredError = Class.new(RegistryError)
-
-
1
def initialize
-
@model_class_to_target = {}
-
end
-
-
1
def lookup_target_class_in_registry!(model_class)
-
lookup_target_class_in_registry(model_class) or raise UnregisteredError, "Unable to locate for #{model_class.name.inspect}"
-
end
-
-
1
def lookup_target_class_through_model_hierarchy!(model_class, root_lookup_model_class = model_class)
-
raise UnregisteredError, "Unable to locate for #{root_lookup_model_class.name.inspect} (#{self.inspect})" if model_class.nil? or ActiveRecord::Base == model_class
-
-
target_class = lookup_target_class_in_registry(model_class)
-
return target_class unless target_class.nil?
-
-
register(model_class, lookup_target_class_through_model_hierarchy!(model_class.superclass, root_lookup_model_class))
-
end
-
-
1
alias_method(:lookup, :lookup_target_class_through_model_hierarchy!)
-
1
private :lookup_target_class_in_registry!
-
1
private :lookup_target_class_through_model_hierarchy!
-
-
1
def lookup_for_class(model_class)
-
lookup(model_class)
-
end
-
-
1
def lookup_for_object(model_instance)
-
lookup(model_instance.class)
-
end
-
-
1
def inspect
-
Hash[@model_class_to_target.map { |k,v| [k.to_s, v.to_s] }].inspect
-
end
-
-
1
def is_already_registered?(model_class)
-
@model_class_to_target.key?(model_class.name)
-
end
-
1
private :is_already_registered?
-
-
1
def register(model_class, io_class)
-
raise StandardError, "Weird class (#{model_class.name.inspect} => #{model_class.inspect})" unless model_class.name =~ /^[A-Z][A-Za-z0-9:]+$/
-
@model_class_to_target[model_class.name] = io_class
-
end
-
1
private :register
-
-
1
def deregister(model_class)
-
@model_class_to_target.delete(model_class.name)
-
end
-
1
private :deregister
-
-
1
def lookup_target_class_in_registry(model_class)
-
@model_class_to_target[model_class.name]
-
end
-
1
private :lookup_target_class_in_registry
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
require 'sinatra/base'
-
module Core
-
class Service < Sinatra::Base
-
API_VERSION = 1
-
-
class Error < StandardError
-
module Behaviour
-
def self.included(base)
-
base.class_eval do
-
class_attribute :api_error_code
-
class_attribute :api_error_message
-
alias_method :api_error_message, :message
-
self.api_error_code = 500
-
end
-
end
-
-
def api_error(response)
-
response.general_error(self.class.api_error_code, [ self.class.api_error_message || self.api_error_message ])
-
end
-
end
-
-
include Behaviour
-
end
-
-
class UnsupportedAction < Error
-
self.api_error_code = 501
-
self.api_error_message = 'requested action is not supported on this resource'
-
end
-
-
class MethodNotAllowed < Error
-
def initialize(allowed_http_verbs)
-
super('HTTP verb was not allowed!')
-
@allowed = Array(allowed_http_verbs).map(&:to_s).map(&:upcase).join(',')
-
end
-
-
self.api_error_code = 405
-
self.api_error_message = 'unsupported action'
-
-
def api_error(response)
-
response.headers('Allow' => @allowed)
-
super
-
end
-
end
-
-
# Report the performance and status of any request
-
def report(handler, &block)
-
Rails.logger.info("API[start]: #{handler}: #{request.fullpath}")
-
yield
-
ensure
-
Rails.logger.info("API[handled]: #{handler}: #{request.fullpath}")
-
end
-
private :report
-
-
# Disable the Sinatra rubbish that happens in the development environment because we want
-
# Rails to do all of the handling if we don't
-
set(:environment, Rails.env)
-
-
# This ensures that our Sinatra applications behave properly within the Rails environment.
-
# Without this you'll find that only one of the services actually behaves properly, the others
-
# will all fail with 404 errors.
-
def handle_not_found!(boom)
-
@response.status = 404
-
@response.headers['X-Cascade'] = 'pass'
-
@response.body = nil
-
end
-
-
# Configure a handler for the cucumber environment that logs the error to the console so that
-
# we can see it.
-
configure :cucumber do
-
error(Exception) do
-
$stderr.puts exception_thrown.message
-
$stderr.puts exception_thrown.backtrace.join("\n")
-
raise exception_thrown
-
end
-
end
-
-
def redirect_to(url, body)
-
status(301) # Moved permanently
-
headers('Location' => url)
-
body(body)
-
end
-
-
def redirect_to_multiple_locations(body)
-
status(300) # Multiple content
-
body(body)
-
end
-
-
def self.api_version_path
-
@version = "api/#{API_VERSION}"
-
end
-
-
def api_path(*sub_path)
-
"#{request.scheme}://#{request.host_with_port}/#{self.class.api_version_path}/#{sub_path.compact.join('/')}"
-
end
-
-
[ :before, :after ].each do |filter|
-
line = __LINE__ + 1
-
class_eval(%Q{
-
def self.#{filter}_all_actions(&block)
-
self.#{filter}(%r{^(/.*)?$}, &block)
-
end
-
}, __FILE__, line)
-
end
-
-
def command
-
@command
-
end
-
-
register Core::Benchmarking
-
register Core::Service::ErrorHandling
-
register Core::Service::Authentication
-
register Core::Service::ContentFiltering
-
-
class Request
-
extend Core::Initializable
-
include Core::References
-
include Core::Benchmarking
-
include Core::Service::EndpointHandling
-
-
initialized_attr_reader :service, :target, :path, :io, :json, :file, :filename
-
attr_writer :io, :file, :filename
-
attr_reader :ability
-
-
delegate :user, :to => :service
-
attr_reader :identifier, :started_at
-
-
def initialize(identifier, *args, &block)
-
@identifier, @started_at = identifier, Time.now
-
super(*args, &block)
-
@ability = Core::Abilities.create(self)
-
end
-
-
def authorisation_code
-
@service.request.env['HTTP_X_SEQUENCESCAPE_CLIENT_ID']
-
end
-
-
def authentication_code
-
# The WTSISignOn service has been retired. However previously the code
-
# supported supplying the API key in this cookie, so this has been left
-
# for compatibility purposes
-
@service.request.cookies['api_key']||@service.request.cookies['WTSISignOn']
-
end
-
-
def response(&block)
-
::Core::Service::Response.new(self, &block)
-
end
-
-
# Safe way to push a particular value on to the request target stack. Ensures that the
-
# original value is reset when the block is exitted.
-
def push(value, &block)
-
target_before, @target = @target, value
-
yield
-
ensure
-
@target = target_before
-
end
-
-
def attributes(object = nil)
-
io.map_parameters_to_attributes(json, nil)
-
end
-
-
def create!(instance_attributes = self.attributes)
-
ActiveRecord::Base.transaction do
-
record = target.create!(instance_attributes)
-
::Core::Io::Registry.instance.lookup_for_object(record).eager_loading_for(record.class).include_uuid.find(record.id)
-
end
-
end
-
-
def update!(instance_attributes = self.attributes(target))
-
ActiveRecord::Base.transaction do
-
target.tap { |o| o.update_attributes!(instance_attributes) }
-
end
-
end
-
end
-
-
include Core::Endpoint::BasicHandler::EndpointLookup
-
-
# A response from an endpoint handler is made of a pair of values. One is the object that
-
# is to be sent back to the client in JSON. The other is the endpoint handler that dealt
-
# with the request and that provides the actions that are available for said object. So
-
# the JSON that is actually returned is a merge of the object JSON and the actions.
-
class Response
-
extend Core::Initializable
-
include Core::References
-
include Core::Benchmarking
-
-
class Initializer
-
delegate :status, :headers, :api_path, :to => '@owner.request.service'
-
-
# Causes a response that will redirect the client to the specified UUID path.
-
def redirect_to(uuid)
-
status(301)
-
headers('Location' => api_path(uuid))
-
end
-
-
# If you want to return multiple records as a kind of "redirect" then this is the
-
# method you want to use!
-
def multiple_choices
-
status(300)
-
end
-
end
-
-
attr_reader :request
-
initialized_attr_reader :handled_by, :object
-
-
delegate :io, :identifier, :started_at, :to => :request
-
-
delegate :status, :to => 'request.service'
-
initialized_delegate :status
-
1
-
delegate :endpoint_for_object, :to => 'request.service'
-
1
private :endpoint_for_object
-
-
1
def initialize(request, &block)
-
@request, @io, @include_actions = request, nil, true
-
status(200)
-
super(&block)
-
end
-
-
#--
-
# Note that this method disables garbage collection, which should improve the performance of writing
-
# out the JSON to the client. The garbage collection is then re-enabled in close.
-
#++
-
1
def each(&block)
-
Rails.logger.info('API[streaming]: starting JSON streaming')
-
start = Time.now
-
-
::Core::Io::Buffer.new(block) do |buffer|
-
::Core::Io::Json::Stream.new(buffer).open do |stream|
-
::Core::Io::Registry.instance.lookup_for_object(object).as_json(
-
:response => self,
-
:target => object,
-
:stream => stream,
-
:object => object,
-
:handled_by => handled_by
-
)
-
end
-
end
-
-
Rails.logger.info("API[streaming]: finished JSON streaming in #{Time.now-start}s")
-
end
-
-
1
def close
-
identifier, started_at = self.identifier, self.started_at # Save for later as next line discards our request!
-
discard_all_references
-
ensure
-
Rails.logger.info("API[finished]: #{identifier} in #{Time.now-started_at}s")
-
end
-
-
1
def discard_all_references
-
request.send(:discard_all_references)
-
super
-
-
# We can also view the current connection as a reference and release that too
-
ActiveRecord::Base.connection_pool.release_connection
-
end
-
1
private :discard_all_references
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
module Core::Service::Authentication
-
1
class UnauthenticatedError < Core::Service::Error
-
1
def self.no_cookie!
-
raise self, 'no authentication provided'
-
end
-
-
1
def self.unauthenticated!
-
raise self, 'could not be authenticated'
-
end
-
-
-
1
def api_error(response)
-
response.general_error(401)
-
end
-
end
-
-
1
module Helpers
-
1
def user
-
@user
-
end
-
end
-
-
1
def self.registered(app)
-
1
app.helpers Helpers
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013 Genome Research Ltd.
-
# Everything coming in and going out should be JSON.
-
1
module Core::Service::ContentFiltering
-
1
class InvalidRequestedContentType < ::Core::Service::Error
-
1
self.api_error_code = 406
-
1
self.api_error_message = "the 'Accept' header can only be 'application/json' or a supported filetype eg.'sequencescape/qc_file'"
-
end
-
-
1
class InvalidRequestedContentTypeOnFile < ::Core::Service::Error
-
1
self.api_error_code = 406
-
1
self.api_error_message = "the 'Accept' header can only be 'application/json' when submitting a file"
-
end
-
-
1
class InvalidBodyContentType < ::Core::Service::Error
-
1
self.api_error_code = 415
-
1
self.api_error_message = "the 'Content-Type' can only be 'application/json' or a supported filetype eg.'sequencescape/qc_file'"
-
end
-
-
1
module Helpers
-
1
def json
-
@json
-
end
-
-
1
def process_request_body
-
content = request.body.read
-
raise Core::Service::ContentFiltering::InvalidBodyContentType if not content.blank? and !acceptable_types.include?(request.content_type)
-
@json = content.blank? ? {} : MultiJson.load(content) if request.content_type == 'application/json' || content.blank?
-
ensure
-
# It's important to ensure that the body IO object has been rewound to the start for other requests.
-
request.body.rewind
-
end
-
-
1
def process_response_body
-
headers('Content-Type' => request_accepted)
-
end
-
-
1
def process_json_response_body
-
headers('Content-Type' => 'application/json')
-
end
-
-
1
ACCEPTABLE_TYPES = [ 'application/json' ]
-
1
ACCEPTABLE_TYPES << '*/*' if Rails.env == 'development'
-
-
1
def acceptable_types
-
ACCEPTABLE_TYPES + ::Api::EndpointHandler.registered_mimetypes
-
end
-
-
1
def check_acceptable_content_type_requested!
-
accepts_json_or_star = !request.acceptable_media_types.prioritize(*acceptable_types).blank?
-
raise Core::Service::ContentFiltering::InvalidRequestedContentType unless accepts_json_or_star
-
end
-
-
1
def request_accepted
-
request.acceptable_media_types.prioritize(*acceptable_types).map(&:to_s)
-
end
-
-
end
-
-
1
def self.registered(app)
-
1
app.helpers Helpers
-
-
1
app.before_all_actions do
-
check_acceptable_content_type_requested!
-
process_request_body
-
end
-
-
1
app.after_all_actions do
-
process_response_body
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module Core::Service::EndpointHandling
-
1
def self.included(base)
-
1
base.class_eval do
-
1
attr_reader :endpoint
-
end
-
end
-
-
1
def instance(action, endpoint)
-
benchmark('Instance handling') do
-
@endpoint = endpoint
-
@endpoint.instance_handler.send(action, self, path)
-
end
-
end
-
-
1
def model(action, endpoint)
-
benchmark('Model handling') do
-
@endpoint = endpoint
-
@endpoint.model_handler.send(action, self, path)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Core::Service::ErrorHandling
-
1
def self.registered(app)
-
1
app.instance_eval do
-
1
helpers Helpers
-
-
# We need hierarchical exception handling, so we rewrite the @errors Hash with our own implementation
-
1
@errors = HierarchicalExceptionMap.new(@errors)
-
-
1
error([
-
::IllegalOperation,
-
::Core::Service::Error,
-
ActiveRecord::ActiveRecordError
-
]) do
-
buffer = [ exception_thrown.message, exception_thrown.backtrace ].join("\n")
-
Rails.logger.error("API[error]: #{buffer}")
-
-
exception_thrown.api_error(self)
-
end
-
1
error([ ::Exception ]) do
-
buffer = [ exception_thrown.message, exception_thrown.backtrace ].join("\n")
-
Rails.logger.error("API[error]: #{buffer}")
-
-
self.general_error(501)
-
end
-
end
-
end
-
-
1
module Helpers
-
1
class JsonError
-
1
def initialize(error)
-
@error = error
-
end
-
-
1
def each(&block)
-
yield JSON.generate(@error)
-
#Yajl::Encoder.new.encode(@error, &block)
-
end
-
end
-
-
1
def exception_thrown
-
@env['sinatra.error']
-
end
-
-
1
def general_error(code, errors = nil)
-
errors ||= [ exception_thrown.message ]
-
error(code, JsonError.new(:general => errors))
-
end
-
-
1
def content_error(code, errors = nil)
-
error(code, JsonError.new(:content => errors))
-
end
-
end
-
-
1
class HierarchicalExceptionMap < Hash
-
1
def initialize(hash)
-
1
super
-
1
merge!(hash || {})
-
end
-
-
1
def [](key)
-
return super[key] unless key.is_a?(Class)
-
key = key.superclass until key.nil? or key?(key)
-
super(key)
-
end
-
end
-
end
-
-
1
class ActiveRecord::RecordNotFound
-
1
include ::Core::Service::Error::Behaviour
-
1
self.api_error_code = 404
-
end
-
-
1
class ActiveRecord::AssociationTypeMismatch
-
1
include ::Core::Service::Error::Behaviour
-
1
self.api_error_code = 422
-
end
-
-
1
class ActiveRecord::StatementInvalid
-
1
include ::Core::Service::Error::Behaviour
-
1
self.api_error_code = 500
-
end
-
-
1
class ActiveRecord::ConfigurationError
-
1
include ::Core::Service::Error::Behaviour
-
1
self.api_error_code = 500
-
end
-
-
1
class ActiveRecord::ReadOnlyRecord
-
1
include ::Core::Service::Error::Behaviour
-
1
self.api_error_code = 500
-
end
-
-
1
class ActiveRecord::RecordInvalid
-
1
def api_error(response)
-
io_handler = ::Core::Io::Registry.instance.lookup_for_object(self.record)
-
response.content_error(422, errors_grouped_by_attribute { |attribute| io_handler.json_field_for(attribute) })
-
end
-
-
1
def errors_grouped_by_attribute
-
Hash[record.errors.map { |k,v| [ yield(k), [v].flatten.uniq ] }]
-
end
-
1
private :errors_grouped_by_attribute
-
end
-
-
1
class ActiveRecord::RecordNotSaved
-
1
def api_error(response)
-
response.content_error(422, message)
-
end
-
end
-
-
1
class IllegalOperation < RuntimeError
-
1
include ::Core::Service::Error::Behaviour
-
1
self.api_error_code = 501
-
1
self.api_error_message = 'requested action is not supported on this resource'
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Endpoints::AssetAudits < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create) do |request, response|
-
request.create!
-
end
-
end
-
-
1
instance do
-
1
belongs_to(:asset, :json => "asset")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::AssetGroups < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
belongs_to(:study, :json => "study")
-
1
belongs_to(:submission, :json => "submission")
-
1
has_many(:assets, :include => [], :json => "assets", :to => "assets")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::Assets < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::BaitLibraryLayouts < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
-
# You can preview a bait library layout, which is effectively an unsaved version of the one
-
# that will be created.
-
1
bind_action(:create, :as => 'preview', :to => 'preview') do |_, request, response|
-
request.target.preview!(request.attributes).tap do |_|
-
response.status(200)
-
end
-
end
-
end
-
-
1
instance do
-
1
belongs_to(:plate, :json => "plate")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Endpoints::BarcodePrinters < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Endpoints::Batches < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
belongs_to(:pipeline, :json => "pipeline")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Endpoints::BulkTransfers < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:user, :json => "user")
-
1
has_many(:transfers, :json => "transfers", :to =>"transfers")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ::Endpoints::Comments < ::Core::Endpoint::Base
-
1
model do
-
end
-
-
1
instance do
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::DilutionPlatePurposes < ::Endpoints::PlatePurposes
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::Lanes < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:requests, :json => 'requests', :to => 'requests')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::LibraryCreationRequests < ::Endpoints::Requests
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Endpoints::LibraryTubes < ::Endpoints::Tubes
-
1
instance do
-
1
has_many(:requests, :json => 'requests', :to => 'requests')
-
1
belongs_to(:source_request, :json => 'source_request')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::LotTypes < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:lots, :json=>'lots', :to=>'lots') do
-
1
action(:create) do |request,_|
-
ActiveRecord::Base.transaction do
-
request.target.proxy_association.owner.create!(request.attributes)
-
end
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::Lots < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:qcables, :json => 'qcables', :to => 'qcables')
-
1
belongs_to(:lot_type, :json => 'lot_type', :to => 'lot_type')
-
1
belongs_to(:template, :json => 'template', :to => 'template')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::MultiplexedLibraryCreationRequests < ::Endpoints::LibraryCreationRequests
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2014 Genome Research Ltd.
-
1
class ::Endpoints::MultiplexedLibraryTubes < ::Endpoints::LibraryTubes
-
-
1
instance do
-
1
has_many(:requests, :json => 'requests', :to => 'requests')
-
1
has_many(:qc_files, :json => 'qc_files', :to => 'qc_files', :include=>[]) do
-
1
action(:create, :as=>'create') do |request, _|
-
ActiveRecord::Base.transaction do
-
QcFile.create!(request.attributes.merge({:asset=>request.target}))
-
end
-
end
-
1
action(:create_from_file, :as => 'create') do |request,_|
-
ActiveRecord::Base.transaction do
-
request.target.add_qc_file(request.file,request.filename)
-
end
-
end
-
end
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
1
class Endpoints::OrderTemplates < Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
nested('orders') do
-
1
action(:create) do |request, _|
-
ActiveRecord::Base.transaction do
-
attributes = ::Io::Order.map_parameters_to_attributes(request.json)
-
attributes.merge!(:user => request.user) if request.user.present?
-
request.target.create_order!(attributes)
-
end
-
end
-
end
-
end
-
end
-
-
# Ensure that this template is used for the SubmissionTemplate model whilst it exists. Without this we
-
# would need to implement another endpoint and the end users would be confused.
-
1
Endpoints::SubmissionTemplates = Endpoints::OrderTemplates
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014 Genome Research Ltd.
-
1
class Endpoints::Orders < Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
belongs_to(:project, :json => 'project')
-
1
belongs_to(:study, :json => 'study')
-
1
belongs_to(:user, :json => 'user')
-
-
1
action(:update, :to => :standard_update!, :if => :building?)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Endpoints::Pipelines < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(
-
:inbox, :scoped => 'ready_in_storage.full_inbox.order_most_recently_created_first',
-
:include => [],
-
:json => "requests", :to => "requests"
-
)
-
-
1
has_many(
-
:batches, :scoped => 'order_most_recently_updated_first',
-
:include => [],
-
:json => "batches", :to => "batches"
-
) do
-
1
action(:create) do |request, _|
-
ActiveRecord::Base.transaction do
-
request.create!(::Io::Batch.map_parameters_to_attributes(request.json).merge(:user => request.user))
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::PlateConversions < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:target, :json => "target")
-
1
belongs_to(:purpose, :json => "purpose")
-
1
belongs_to(:user, :json => "user")
-
1
belongs_to(:parent, :json => "parent")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::PlateCreations < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:child, :json => "child")
-
1
belongs_to(:child_purpose, :json => "child_purpose")
-
1
belongs_to(:parent, :json => "parent")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::PlatePurposes < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:child_purposes, :json => 'children', :to => 'children')
-
-
1
has_many(:plates, :json => 'plates', :to => 'plates') do
-
1
action(:create) do |request, _|
-
ActiveRecord::Base.transaction do
-
request.target.proxy_association.owner.create!(request.attributes)
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::PlateTemplates < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:wells, :json => 'wells', :to => 'wells', :scoped => 'for_api_plate_json.in_row_major_order')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015 Genome Research Ltd.
-
1
class ::Endpoints::Plates < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:comments, :json => 'comments', :to => 'comments') do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
has_many(:wells, :json => 'wells', :to => 'wells', :scoped => 'for_api_plate_json.in_row_major_order')
-
1
has_many(:submission_pools, :json => 'submission_pools', :to => 'submission_pools' )
-
1
has_many(:requests, :json => 'requests', :to => 'requests')
-
1
belongs_to(:plate_purpose, :json => 'plate_purpose')
-
-
1
has_many(:qc_files, :json => 'qc_files', :to => 'qc_files', :include=>[]) do
-
1
action(:create, :as=>'create') do |request, _|
-
ActiveRecord::Base.transaction do
-
QcFile.create!(request.attributes.merge({:asset=>request.target}))
-
end
-
end
-
1
action(:create_from_file, :as => 'create') do |request,_|
-
ActiveRecord::Base.transaction do
-
request.target.add_qc_file(request.file,request.filename)
-
end
-
end
-
end
-
-
1
has_many(:transfers_as_source, :json => 'source_transfers', :to => 'source_transfers')
-
1
has_many(:transfers_to_tubes, :json => 'transfers_to_tubes', :to => 'transfers_to_tubes')
-
1
has_many(:transfers_as_destination, :json => 'creation_transfers', :to => 'creation_transfers')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Endpoints::PooledPlateCreations < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:child, :json => "child")
-
1
belongs_to(:child_purpose, :json => "child_purpose")
-
1
has_many(:parents, :json => "parents", :to=> "parents")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Endpoints::Projects < Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:submissions, :json => 'submissions', :to => 'submissions')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::QcDecisions < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create) do |request,_|
-
request.target.create!(request.attributes.tap do |attributes|
-
attributes[:decisions]=(attributes[:decisions]||[]).map do |d|
-
d.merge({'qcable'=>Uuid.find_by_external_id(d['qcable']).resource})
-
end
-
end)
-
end
-
end
-
-
1
instance do
-
1
belongs_to(:user, :json => 'user')
-
1
belongs_to(:lot, :json => 'lot')
-
1
has_many(:qcables, :json => 'qcables', :to=>'qcables')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Endpoints::QcFiles < ::Core::Endpoint::Base
-
1
model do
-
#action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
# belongs_to :plate, :json => 'plate'
-
1
has_file(:content_type=> 'sequencescape/qc_file')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::QcableCreators < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:user, :json => 'user')
-
1
belongs_to(:lot, :json => 'lot')
-
1
has_many(:qcables, :json => 'qcables', :to=>'qcables')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::Qcables < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
belongs_to(:asset, :json => 'asset')
-
1
belongs_to(:lot, :json => 'lot')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::ReferenceGenomes < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
action(:update, :to => :standard_update!)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::RequestTypes < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Endpoints::Requests < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
belongs_to(:asset, :json => 'source_asset')
-
1
belongs_to(:target_asset, :json => 'target_asset')
-
1
belongs_to(:submission, :json => 'submission')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::Robots < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Endpoints::SampleManifests < ::Core::Endpoint::Base
-
1
model do
-
# TODO: For the moment we have to disable the read functionality as it consumes too much memory.
-
# Loading a sample manifest of only a few thousand samples causes the memory to spike at 1.2GB
-
# and when you have 10s of these in a 100 entry page of results that is not good.
-
1
disable :read
-
end
-
-
1
instance do
-
1
belongs_to(:study, :json => "study")
-
1
belongs_to(:supplier, :json => "supplier")
-
-
1
action(:update) do |request, response|
-
ActiveRecord::Base.transaction do
-
request.target.tap do |manifest|
-
manifest.update_attributes!(request.attributes(request.target), request.user)
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Endpoints::SampleTubes < ::Endpoints::Tubes
-
1
instance do
-
1
has_many(:requests, :json => 'requests', :to => 'requests')
-
1
has_many(:library_tubes, :json => 'library_tubes', :to => 'library_tubes')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::Samples < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::Searches < ::Core::Endpoint::Base
-
1
module SearchActions
-
1
def search_action(name, &block)
-
1
bind_action(:create, :to => name.to_s, :as => name.to_sym) do |action, request, response|
-
request.target.scope(request.json['search']).send(name).tap do |results|
-
response.handled_by = action
-
block.call(response, results)
-
end
-
end
-
end
-
-
1
def singular_search_action(name)
-
2
bind_action(:create, :to => name.to_s, :as => name.to_sym) do |action, request, response|
-
record = request.target.scope(request.json['search']).send(name.to_sym)
-
raise ActiveRecord::RecordNotFound, 'no resources found with that search criteria' if record.nil?
-
-
request.io = ::Core::Io::Registry.instance.lookup_for_object(record)
-
request.io.eager_loading_for(record.class).include_uuid.find(record.id).tap do |result|
-
response.redirect_to(record.uuid)
-
end
-
end
-
end
-
end
-
-
1
model do
-
-
end
-
-
1
instance do
-
1
extend SearchActions
-
-
1
singular_search_action(:first)
-
1
singular_search_action(:last)
-
-
1
search_action(:all) do |response, records|
-
response.multiple_choices
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Endpoints::SequencingRequests < ::Endpoints::Requests
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Endpoints::SpecificTubeCreations < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
has_many(:children, :json => "children", :to => "children")
-
1
has_many(:child_purposes, :json => "child_purposes", :to => "child_purposes")
-
1
belongs_to(:parent, :json => "parent")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::Stamps < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:user, :json => 'user')
-
1
belongs_to(:robot, :json => 'robot')
-
1
belongs_to(:lot, :json=>'lot')
-
1
has_many(:qcables, :json => 'qcables', :to => 'qcables')
-
1
has_many(:stamp_qcables, :json => 'stamp_qcables', :to => 'stamp_qcables')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Endpoints::StateChanges < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:target, :json => "target")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
class Endpoints::Studies < Core::Endpoint::Base
-
model do
-
-
end
-
-
instance do
-
has_many(:samples, :json => 'samples', :to => 'samples')
-
has_many(:projects, :json => 'projects', :to => 'projects')
-
has_many(:asset_groups, :json => 'asset_groups', :to => 'asset_groups')
-
-
has_many(:sample_manifests, :json => 'sample_manifests', :to => 'sample_manifests') do
-
def self.constructor(name, method)
-
line = __LINE__ + 1
-
instance_eval(%Q{
-
1
bind_action(:create, :as => #{name.to_sym.inspect}, :to => #{name.to_s.inspect}) do |_, request, response|
-
ActiveRecord::Base.transaction do
-
manifest = request.target.#{method}(request.attributes)
-
request.io.eager_loading_for(manifest.class).include_uuid.find(manifest.id)
-
end
-
end
-
}, __FILE__, line)
-
end
-
-
constructor(:create_for_plates, :create_for_plate!)
-
constructor(:create_for_tubes, :create_for_sample_tube!)
-
constructor(:create_for_multiplexed_libraries, :create_for_multiplexed_library!)
-
end
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Endpoints::SubmissionPools < Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class Endpoints::Submissions < Core::Endpoint::Base
-
1
model do
-
1
action(:create) do |request, _|
-
attributes = ::Io::Submission.map_parameters_to_attributes(request.json)
-
attributes.merge!(:user => request.user) if request.user.present?
-
request.target.create!(attributes)
-
end
-
end
-
-
1
instance do
-
1
belongs_to(:user, :json => 'user')
-
1
has_many(
-
:requests, :json => 'requests', :to => 'requests',
-
:include => [ :source_asset, :target_asset ]
-
)
-
-
1
action(:update, :to => :standard_update!, :if => :building?)
-
-
1
bind_action(:create, :as => :submit, :to => 'submit', :if => :building?) do |_, request, response|
-
ActiveRecord::Base.transaction do
-
request.target.tap do |submission|
-
submission.built!
-
response.status(200) # OK
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Endpoints::Suppliers < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:sample_manifests, :include => [], :json => "sample_manifests", :to => "sample_manifests")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ::Endpoints::Tag2LayoutTemplates < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
action(:create) do |request, _|
-
ActiveRecord::Base.transaction do
-
request.create!(::Io::Tag2Layout.map_parameters_to_attributes(request.json).reverse_merge(:user => request.user))
-
end
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014,2015 Genome Research Ltd.
-
1
class ::Endpoints::Tag2Layouts < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:plate, :json => "plate")
-
1
belongs_to(:source, :json => "source")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Endpoints::TagGroups < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::TagLayoutTemplates < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
action(:create) do |request, _|
-
ActiveRecord::Base.transaction do
-
request.create!(::Io::TagLayout.map_parameters_to_attributes(request.json).reverse_merge(:user => request.user))
-
end
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014 Genome Research Ltd.
-
1
class ::Endpoints::TagLayouts < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:plate, :json => "plate")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::TransferTemplates < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
def build_transfer(request, &block)
-
ActiveRecord::Base.transaction do
-
# Here we have to map the JSON provided based on the transfer class we're going to build
-
io_handler = ::Core::Io::Registry.instance.lookup_for_class(request.target.transfer_class)
-
ActiveRecord::Base.transaction do
-
yield(io_handler.map_parameters_to_attributes(request.json).reverse_merge(:user => request.user))
-
end
-
end
-
end
-
-
1
action(:create) do |request,response|
-
response.status(201)
-
build_transfer(request, &request.target.method(:create!))
-
end
-
1
bind_action(:create, :as => 'preview', :to => 'preview') do |_,request,response|
-
response.status(200)
-
build_transfer(request, &request.target.method(:preview!))
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Endpoints::Transfers < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ::Endpoints::Tube::Purposes < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:child_purposes, :json => 'children', :to => 'children')
-
1
has_many(:tubes, :json => 'tubes', :to => 'tubes')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ::Endpoints::TubeCreations < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
has_many(:children, :json => "children", :to => "children")
-
1
belongs_to(:child_purpose, :json => "child_purpose")
-
1
belongs_to(:parent, :json => "parent")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Endpoints::TubeFromTubeCreations < ::Core::Endpoint::Base
-
1
model do
-
1
action(:create, :to => :standard_create!)
-
end
-
-
1
instance do
-
1
belongs_to(:child, :json => "child", :to => "child")
-
1
belongs_to(:child_purpose, :json => "child_purpose")
-
1
belongs_to(:parent, :json => "parent")
-
1
belongs_to(:user, :json => "user")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013 Genome Research Ltd.
-
1
class ::Endpoints::Tubes < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
has_many(:requests, :json => 'requests', :to => 'requests')
-
1
belongs_to(:purpose, :json => 'purpose')
-
-
1
has_many(:qc_files, :json => 'qc_files', :to => 'qc_files', :include=>[]) do
-
1
action(:create, :as=>'create') do |request, _|
-
ActiveRecord::Base.transaction do
-
QcFile.create!(request.attributes.merge({:asset=>request.target}))
-
end
-
end
-
1
action(:create_from_file, :as => 'create') do |request,_|
-
ActiveRecord::Base.transaction do
-
request.target.add_qc_file(request.file,request.filename)
-
end
-
end
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Endpoints::Users < ::Core::Endpoint::Base
-
1
model do
-
end
-
-
1
instance do
-
1
action(:update, :to => :standard_update!)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
#--
-
# This is a complete hack of the standard behaviour and quite rightly so: people shouldn't be using it and
-
# so it is going to go. Rather than pollute the main API code with this rubbish it's here.
-
#++
-
class ::Endpoints::Uuids < ::Core::Endpoint::Base
-
module Response
-
def redirect_to(path)
-
@owner.request.service.status(301)
-
@owner.request.service.headers('Location' => path)
-
render_body_json_directly
-
end
-
-
def multiple_choices
-
@owner.request.service.status(300)
-
render_body_json_directly
-
end
-
-
def render_body_json_directly
-
def @owner.each
-
yield(object.to_json)
-
end
-
end
-
private :render_body_json_directly
-
end
-
-
class Search
-
class CriteriaInvalid < ::Core::Service::Error
-
def initialize(*args)
-
super
-
@errors = { :lookup => [ self.message ] }
-
end
-
-
def api_error(response)
-
response.content_error(422, @errors)
-
end
-
end
-
-
include ::Validateable
-
-
attr_reader :lookup
-
protected :lookup
-
validates_presence_of :lookup, :message => 'should be a tuple'
-
validates_each(:lookup, :allow_blank => true) do |record, field, value|
-
record.errors.add(field, 'should be a tuple') unless value.is_a?(Hash)
-
end
-
-
def self.attribute_delegate(*names)
-
names.each do |name|
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{name}
-
return nil unless lookup.respond_to?(:fetch)
-
lookup[#{name.to_s.inspect}]
-
end
-
1
protected #{name.to_sym.inspect}
-
}, __FILE__, line)
-
end
-
end
-
-
attribute_delegate(:id, :model)
-
1
validates_numericality_of :id, :only_integer => true, :greater_than => 0, :allow_blank? => false
-
1
validates_presence_of :model
-
-
1
def initialize(attributes)
-
@lookup = attributes
-
end
-
-
1
def self.create!(attributes)
-
search = self.new(attributes)
-
search.validate! {}
-
search
-
end
-
-
1
def self.create_bulk!(list_of_attributes)
-
raise CriteriaInvalid, "should be an array of tuples" if list_of_attributes.nil?
-
raise CriteriaInvalid, "should be an array of tuples" unless list_of_attributes.is_a?(Array)
-
raise CriteriaInvalid, "can't be blank" if list_of_attributes.blank?
-
raise CriteriaInvalid, "should be a tuple" unless list_of_attributes.all? { |a| a.is_a?(Hash) }
-
-
list_of_attributes.map(&method(:create!))
-
end
-
-
1
def find
-
Uuid.find_uuid_instance!(model.classify, id)
-
end
-
end
-
-
1
model do
-
# You should not be able to read UUIDs
-
1
disable(:read)
-
-
# Does an individual resource lookup
-
1
bind_action(:create, :to => 'lookup', :as => :lookup) do |_,request, response|
-
lookup = request.json.respond_to?(:keys) ? request.json['lookup'] : nil
-
uuid = Search.create!(lookup).find
-
-
# Hack time ...
-
class << response ; include ::Endpoints::Uuids::Response ; end
-
response.redirect_to(request.service.api_path(uuid.external_id))
-
-
{
-
'model' => uuid.resource_type.underscore,
-
'id' => uuid.resource_id,
-
'uuid' => uuid.external_id,
-
'url' => request.service.api_path(uuid.external_id)
-
}
-
end
-
1
bound_action_does_not_require_an_io_class(:lookup)
-
-
# Handles trying to find multiple resources
-
1
bind_action(:create, :to => 'bulk', :as => :bulk) do |_,request, response|
-
lookup = request.json.respond_to?(:keys) ? request.json['lookup'] : nil
-
uuids = Search.create_bulk!(lookup).map(&:find)
-
-
# Hack time ...
-
class << response ; include ::Endpoints::Uuids::Response ; end
-
response.multiple_choices
-
-
uuids.map do |uuid|
-
{
-
'model' => uuid.resource_type.underscore,
-
'id' => uuid.resource_id,
-
'uuid' => uuid.external_id,
-
'url' => request.service.api_path(uuid.external_id)
-
}
-
end
-
end
-
1
bound_action_does_not_require_an_io_class(:bulk)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ::Endpoints::Wells < ::Core::Endpoint::Base
-
1
model do
-
-
end
-
-
1
instance do
-
1
belongs_to :plate, :json => 'plate'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::ActiveRecord::Relation
-
1
extend ::Core::Io::Collection
-
-
1
class << self
-
1
def as_json(options = {})
-
options[:handled_by].generate_json_actions(options[:object], options.merge(:target => options[:response].request.target))
-
super
-
end
-
-
1
def size_for(results)
-
results.total_entries
-
end
-
1
private :size_for
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class Io::Aliquot < Core::Io::Base
-
1
set_model_for_input(::Aliquot)
-
1
set_json_root(:aliquot)
-
-
1
define_attribute_and_json_mapping(%Q{
-
sample => sample
-
-
tag.name => tag.name
-
tag.map_id => tag.identifier
-
tag.oligo => tag.oligo
-
tag.tag_group.name => tag.group
-
-
bait_library => bait_library
-
-
insert_size.from => insert_size.from
-
insert_size.to => insert_size.to
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::Array
-
1
extend Core::Io::Collection
-
-
1
def self.size_for(collection)
-
collection.size
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
class Io::Asset < Core::Io::Base
-
1
set_model_for_input(::Asset)
-
1
set_json_root(:asset)
-
1
set_eager_loading { |model| model.include_barcode_prefix }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
qc_state => qc_state
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::AssetAudit < ::Core::Io::Base
-
# This module adds the behaviour we require from the AssetAudit module.
-
1
module ApiIoSupport
-
1
def self.included(base)
-
1
base.class_eval do
-
# TODO: add any named scopes
-
# TODO: add any associations
-
end
-
end
-
-
1
def asset_uuid
-
self.asset.try(:uuid)
-
end
-
-
1
def asset_uuid=(uuid)
-
self.asset = Uuid.with_external_id(uuid).include_resource.map(&:resource).first
-
end
-
-
# TODO: add any methods
-
end
-
-
1
set_model_for_input(::AssetAudit)
-
1
set_json_root(:asset_audit)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
# TODO: define the mapping from the model attributes to the JSON attributes
-
#
-
# The rules are relatively straight forward with each line looking like '<attibute> <access> <json>', and blank lines or
-
# those starting with '#' being considered comments and ignored.
-
#
-
# Here 'access' is either '=>' (for read only, indicating that the 'attribute' maps to the 'json'), or '<=' for write only (yes,
-
# there are cases for this!) or '<=>' for read-write.
-
#
-
# The 'json' is the JSON attribute to generate in dot notation, i.e. 'parent.child' generates the JSON '{parent:{child:value}}'.
-
#
-
# The 'attribute' is the attribute to write, i.e. 'name' would be the 'name' attribute, and 'parent.name' would be the 'name'
-
# attribute of whatever 'parent' is.
-
-
-
-
1
define_attribute_and_json_mapping(%Q{
-
message <=> message
-
key <=> key
-
created_by <=> created_by
-
asset_uuid <=> asset
-
witnessed_by <=> witnessed_by
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::AssetGroup < ::Core::Io::Base
-
1
set_model_for_input(::AssetGroup)
-
1
set_json_root(:asset_group)
-
1
set_eager_loading { |model| model.include_study.include_assets }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
study.name => study.name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class Io::BaitLibrary < Core::Io::Base
-
1
set_model_for_input(::BaitLibrary)
-
1
set_json_root(:bait_library)
-
-
1
define_attribute_and_json_mapping(%Q{
-
bait_library_supplier.name => supplier.name
-
supplier_identifier => supplier.identifier
-
name => name
-
target_species => target.species
-
bait_library_type.name => bait_library_type
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::BaitLibraryLayout < ::Core::Io::Base
-
1
set_model_for_input(::BaitLibraryLayout)
-
1
set_json_root(:bait_library_layout)
-
1
set_eager_loading { |model| model.include_plate }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
plate <=> plate
-
well_layout => layout
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::BarcodePrinter < ::Core::Io::Base
-
1
set_model_for_input(::BarcodePrinter)
-
1
set_json_root(:barcode_printer)
-
1
set_eager_loading { |model| model.include_barcode_printer_type }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
active => active
-
service_url => service.url
-
barcode_printer_type.name => type.name
-
barcode_printer_type.printer_type_id => type.layout
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::Batch < ::Core::Io::Base
-
1
set_model_for_input(::Batch)
-
1
set_json_root(:batch)
-
1
set_eager_loading { |model| model.include_user.include_requests.include_pipeline }
-
-
1
define_attribute_and_json_mapping(%Q{
-
state => state
-
production_state => production_state
-
qc_state => qc_state
-
barcode => barcode
-
user.login => user.login
-
-
requests <=> requests
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::BulkTransfer < ::Core::Io::Base
-
1
set_model_for_input(::BulkTransfer)
-
1
set_json_root(:bulk_transfer)
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
well_transfers <= well_transfers
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Io::Comment < ::Core::Io::Base
-
1
set_model_for_input(::Comment)
-
1
set_json_root(:comment)
-
1
set_eager_loading { |model| model }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <= user
-
title <=> title
-
description <=> description
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Io::DilutionPlatePurpose < Io::PlatePurpose
-
1
set_model_for_input(::DilutionPlatePurpose)
-
1
set_json_root(:dilution_plate_purpose)
-
-
1
define_attribute_and_json_mapping(%Q{
-
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::Endpoints::Uuids::Search
-
1
def self.model_for_input
-
::Uuids::Search
-
end
-
-
1
def initialize(search)
-
@search = search
-
end
-
-
1
def self.json_field_for(attribute)
-
attribute
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Io::Lane < Io::Asset
-
1
set_model_for_input(::Lane)
-
1
set_json_root(:lane)
-
#set_eager_loading { |model| model.include_barcode_prefix }
-
-
1
define_attribute_and_json_mapping(%Q{
-
external_release => external_release
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Io::LibraryCreationRequest < ::Io::Request
-
1
set_model_for_input(::LibraryCreationRequest)
-
1
set_json_root(:library_creation_request)
-
-
1
define_attribute_and_json_mapping(%Q{
-
request_metadata.library_type => library_type
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Io::LibraryTube < Io::Tube
-
1
set_model_for_input(::LibraryTube)
-
1
set_json_root(:library_tube)
-
1
set_eager_loading { |model| model.include_source_request }
-
-
1
define_attribute_and_json_mapping(%Q{
-
source_request.request_metadata.read_length => source_request.read_length
-
source_request.request_metadata.library_type => source_request.library_type
-
source_request.request_metadata.fragment_size_required_from => source_request.fragment_size.from
-
source_request.request_metadata.fragment_size_required_to => source_request.fragment_size.to
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Io::Lot < ::Core::Io::Base
-
1
set_model_for_input(::Lot)
-
1
set_json_root(:lot)
-
-
1
set_eager_loading { |model| model.include_lot_type.include_template }
-
-
1
define_attribute_and_json_mapping(%Q{
-
lot_number <=> lot_number
-
received_at <=> received_at
-
template.name => template_name
-
lot_type.name => lot_type_name
-
lot_type <= lot_type
-
user <= user
-
template <= template
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
class ::Io::LotType < ::Core::Io::Base
-
1
set_model_for_input(::LotType)
-
1
set_json_root(:lot_type)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
template_class => template_class
-
target_purpose.name => qcable_name
-
printer_type => printer_type
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Io::MultiplexedLibraryCreationRequest < ::Io::LibraryCreationRequest
-
1
set_model_for_input(::MultiplexedLibraryCreationRequest)
-
1
set_json_root(:multiplexed_library_creation_request)
-
1
define_attribute_and_json_mapping('')
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Io::MultiplexedLibraryTube < ::Io::LibraryTube
-
1
set_model_for_input(::MultiplexedLibraryTube)
-
1
set_json_root(:multiplexed_library_tube)
-
-
1
define_attribute_and_json_mapping('')
-
# TODO: Find an efficient way to display state as it kills transfers_to_tubes for plates!
-
# define_attribute_and_json_mapping(%Q{
-
# state => state
-
# })
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014 Genome Research Ltd.
-
1
class ::Io::Order < ::Core::Io::Base
-
1
REQUEST_OPTIONS_FIELDS = Hash[{
-
:read_length => 'read_length',
-
:library_type => 'library_type',
-
:fragment_size_required_from => 'fragment_size_required.from',
-
:fragment_size_required_to => 'fragment_size_required.to'
-
4
}.map { |k,v| [ "request_options.#{k}".to_sym, "request_options.#{v}"] }]
-
-
1
def self.json_field_for(attribute)
-
REQUEST_OPTIONS_FIELDS[attribute.to_sym] || super
-
end
-
-
1
set_model_for_input(::Order)
-
1
set_json_root(:order)
-
1
set_eager_loading { |model| model.include_study.include_project.include_assets }
-
-
1
define_attribute_and_json_mapping(%Q{
-
study <= study
-
study.name => study.name
-
-
project <= project
-
project.name => project.name
-
-
asset_group <= asset_group
-
asset_group_name <= asset_group_name
-
-
assets <=> assets
-
-
request_type_objects => request_types
-
request_options_structured <=> request_options
-
-
user <= user
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::PacBioLibraryTube < ::Io::Asset
-
1
set_model_for_input(::PacBioLibraryTube)
-
1
set_json_root(:pac_bio_library_tube)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
pac_bio_library_tube_metadata.prep_kit_barcode <=> prep_kit_barcode
-
pac_bio_library_tube_metadata.binding_kit_barcode <=> binding_kit_barcode
-
pac_bio_library_tube_metadata.smrt_cells_available <=> smrt_cells_available
-
pac_bio_library_tube_metadata.movie_length <=> movie_length
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ::Io::Pipeline < ::Core::Io::Base
-
1
set_model_for_input(::Pipeline)
-
1
set_json_root(:pipeline)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014,2015 Genome Research Ltd.
-
1
class Io::Plate < Io::Asset
-
1
set_model_for_input(::Plate)
-
1
set_json_root(:plate)
-
1
set_eager_loading { |model| model.include_plate_purpose }
-
-
1
define_attribute_and_json_mapping(%Q{
-
size <=> size
-
plate_purpose.name => plate_purpose.name
-
plate_purpose.lifespan => plate_purpose.lifespan
-
-
state => state
-
iteration => iteration
-
pools => pools
-
pre_cap_groups => pre_cap_groups
-
role => label.prefix
-
purpose.name => label.text
-
location.name => location
-
priority => priority
-
-
source_plate.uuid => stock_plate.uuid
-
source_plate.barcode => stock_plate.barcode.number
-
source_plate.barcode_prefix.prefix => stock_plate.barcode.prefix
-
source_plate.two_dimensional_barcode => stock_plate.barcode.two_dimensional
-
source_plate.ean13_barcode => stock_plate.barcode.ean13
-
source_plate.barcode_type => stock_plate.barcode.type
-
-
barcode => barcode.number
-
barcode_prefix.prefix => barcode.prefix
-
two_dimensional_barcode => barcode.two_dimensional
-
ean13_barcode => barcode.ean13
-
barcode_type => barcode.type
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Io::PlateConversion < ::Core::Io::Base
-
1
set_model_for_input(::PlateConversion)
-
1
set_json_root(:plate_conversion)
-
1
set_eager_loading { |model| model }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
target <=> target
-
purpose <=> purpose
-
parent <= parent
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Io::PlateCreation < ::Core::Io::Base
-
1
set_model_for_input(::PlateCreation)
-
1
set_json_root(:plate_creation)
-
1
set_eager_loading { |model| model.include_parent.include_child }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
parent <=> parent
-
child_purpose <=> child_purpose
-
child => child
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Io::PlatePurpose < Core::Io::Base
-
1
set_model_for_input(::PlatePurpose)
-
1
set_json_root(:plate_purpose)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Io::PlateTemplate < Io::Asset
-
1
set_model_for_input(::PlateTemplate)
-
1
set_json_root(:plate_template)
-
-
1
define_attribute_and_json_mapping(%Q{
-
size <=> size
-
name <=> name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::PooledPlateCreation < ::Core::Io::Base
-
1
set_model_for_input(::PooledPlateCreation)
-
1
set_json_root(:pooled_plate_creation)
-
#set_eager_loading { |model| model.include_parents.include_child }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
parents <= parents
-
child_purpose <=> child_purpose
-
child => child
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Io::Project < Core::Io::Base
-
1
set_model_for_input(::Project)
-
1
set_json_root(:project)
-
1
set_eager_loading { |model| model.include_project_metadata.include_roles }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
approved => approved
-
state => state
-
-
project_metadata.project_manager.name => project_manager
-
project_metadata.project_cost_code => cost_code
-
project_metadata.funding_comments => funding_comments
-
project_metadata.collaborators => collaborators
-
project_metadata.external_funding_source => external_funding_source
-
project_metadata.budget_division.name => budget_division
-
project_metadata.sequencing_budget_cost_centre => budget_cost_centre
-
project_metadata.project_funding_model => funding_model
-
-
roles_as_json => roles
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Io::Purpose < Core::Io::Base
-
1
set_model_for_input(::Purpose)
-
1
set_json_root(:purpose)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Io::QcDecision < Core::Io::Base
-
1
set_model_for_input(::QcDecision)
-
1
set_json_root(:qc_decision)
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
lot <=> lot
-
decisions <= decisions
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::QcFile < ::Core::Io::Base
-
1
set_model_for_input(::QcFile)
-
1
set_json_root(:qc_file)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
filename => filename
-
size => size
-
})
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Io::Qcable < Core::Io::Base
-
1
set_model_for_input(::Qcable)
-
1
set_json_root(:qcable)
-
-
1
set_eager_loading { |model| model.include_for_json }
-
-
1
define_attribute_and_json_mapping(%Q{
-
state => state
-
stamp_qcable.bed => stamp_bed
-
stamp_index => stamp_index
-
-
asset.barcode => barcode.number
-
asset.barcode_prefix.prefix => barcode.prefix
-
asset.ean13_barcode => barcode.ean13
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Io::QcableCreator < Core::Io::Base
-
1
set_model_for_input(::QcableCreator)
-
1
set_json_root(:qcable_creator)
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
lot <=> lot
-
count <= count
-
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Io::ReferenceGenome < ::Core::Io::Base
-
1
set_model_for_input(::ReferenceGenome)
-
1
set_json_root(:reference_genome)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name <=> name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Io::Request < ::Core::Io::Base
-
1
set_model_for_input(::Request)
-
1
set_json_root(:request)
-
1
set_eager_loading do |model|
-
model.
-
include_request_type.include_request_metadata.
-
include_submission.
-
include_source_asset.include_target_asset
-
end
-
-
1
define_attribute_and_json_mapping(%Q{
-
request_type.name => type
-
request_metadata.fragment_size_required_from => fragment_size.from
-
request_metadata.fragment_size_required_to => fragment_size.to
-
state <=> state
-
-
submission.uuid => submission.uuid
-
-
asset <= source_asset
-
asset.sti_type.tableize => source_asset.type
-
asset.name => source_asset.name
-
asset.aliquots => source_asset.aliquots
-
-
target_asset <= target_asset
-
target_asset.sti_type.tableize => target_asset.type
-
target_asset.name => target_asset.name
-
target_asset.aliquots => target_asset.aliquots
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Io::RequestType < ::Core::Io::Base
-
1
set_model_for_input(::RequestType)
-
1
set_json_root(:request_type)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Io::Robot < ::Core::Io::Base
-
1
set_model_for_input(::Robot)
-
1
set_json_root(:robot)
-
-
1
set_eager_loading { |model| model.include_properties }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
json_for_properties => robot_properties
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Io::Sample < Core::Io::Base
-
1
set_model_for_input(::Sample)
-
1
set_json_root(:sample)
-
1
set_eager_loading { |model| model.include_sample_metadata.include_studies }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => sanger.name
-
sanger_sample_id => sanger.sample_id
-
sample_metadata.is_resubmitted <=> sanger.resubmitted
-
sample_metadata.sample_description <=> sanger.description
-
-
sample_metadata.supplier_name <=> supplier.sample_name
-
sample_metadata.sample_storage_conditions <=> supplier.storage_conditions
-
-
sample_metadata.date_of_sample_collection <=> supplier.collection.date
-
-
sample_metadata.date_of_sample_extraction <=> supplier.extraction.date
-
sample_metadata.sample_extraction_method <=> supplier.extraction.method
-
-
sample_metadata.sample_purified <=> supplier.purification.purified
-
sample_metadata.purification_method <=> supplier.purification.method
-
-
sample_metadata.volume <=> supplier.measurements.volume
-
sample_metadata.concentration <=> supplier.measurements.concentration
-
sample_metadata.gc_content <=> supplier.measurements.gc_content
-
sample_metadata.gender <=> supplier.measurements.gender
-
sample_metadata.concentration_determined_by <=> supplier.measurements.concentration_determined_by
-
-
sample_metadata.dna_source <=> source.dna_source
-
sample_metadata.cohort <=> source.cohort
-
sample_metadata.country_of_origin <=> source.country
-
sample_metadata.geographical_region <=> source.region
-
sample_metadata.ethnicity <=> source.ethnicity
-
control <=> source.control
-
-
sample_metadata.mother <=> family.mother
-
sample_metadata.father <=> family.father
-
sample_metadata.replicate <=> family.replicate
-
sample_metadata.sibling <=> family.sibling
-
-
sample_metadata.sample_taxon_id <=> taxonomy.id
-
sample_metadata.sample_strain_att <=> taxonomy.strain
-
sample_metadata.sample_common_name <=> taxonomy.common_name
-
sample_metadata.organism <=> taxonomy.organism
-
sample_reference_genome_name <=> reference.genome
-
-
sample_metadata.sample_ebi_accession_number <=> data_release.accession_number
-
sample_metadata.sample_type <=> data_release.sample_type
-
-
sample_metadata.sample_sra_hold => data_release.visibility
-
sample_metadata.sample_public_name <=> data_release.public_name
-
sample_metadata.sample_description <=> data_release.description
-
-
sample_metadata.genotype <=> data_release.metagenomics.genotype
-
sample_metadata.phenotype <=> data_release.metagenomics.phenotype
-
sample_metadata.age <=> data_release.metagenomics.age
-
sample_metadata.developmental_stage <=> data_release.metagenomics.developmental_stage
-
sample_metadata.cell_type <=> data_release.metagenomics.cell_type
-
sample_metadata.disease_state <=> data_release.metagenomics.disease_state
-
sample_metadata.compound <=> data_release.metagenomics.compound
-
sample_metadata.dose <=> data_release.metagenomics.dose
-
sample_metadata.immunoprecipitate <=> data_release.metagenomics.immunoprecipitate
-
sample_metadata.growth_condition <=> data_release.metagenomics.growth_condition
-
sample_metadata.rnai <=> data_release.metagenomics.rnai
-
sample_metadata.organism_part <=> data_release.metagenomics.organism_part
-
sample_metadata.time_point <=> data_release.metagenomics.time_point
-
sample_metadata.treatment <=> data_release.metagenomics.treatment
-
sample_metadata.subject <=> data_release.metagenomics.subject
-
sample_metadata.disease <=> data_release.metagenomics.disease
-
-
sample_metadata.treatment <=> data_release.managed.treatment
-
sample_metadata.subject <=> data_release.managed.subject
-
sample_metadata.disease <=> data_release.managed.disease
-
-
library_information <= library_information
-
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Io::SampleManifest < ::Core::Io::Base
-
1
set_model_for_input(::SampleManifest)
-
1
set_json_root(:sample_manifest)
-
1
set_eager_loading { |model| model.include_samples }
-
-
1
define_attribute_and_json_mapping(%Q{
-
override_previous_manifest <= override_previous_manifest
-
last_errors => last_errors
-
state => state
-
supplier <= supplier
-
count <= count
-
-
io_samples => samples
-
samples <= samples
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Io::SampleTube < Io::Tube
-
1
set_model_for_input(::SampleTube)
-
1
set_json_root(:sample_tube)
-
-
1
define_attribute_and_json_mapping(%Q{})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ::Io::Search < ::Core::Io::Base
-
1
set_json_root(:search)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Io::SequencingRequest < ::Io::Request
-
1
set_model_for_input(::SequencingRequest)
-
1
set_json_root(:sequencing_request)
-
-
1
define_attribute_and_json_mapping(%Q{
-
request_metadata.read_length => read_length
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::SpecificTubeCreation < ::Core::Io::Base
-
1
set_model_for_input(::SpecificTubeCreation)
-
1
set_json_root(:specific_tube_creation)
-
1
set_eager_loading { |model| model.include_parent }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
parent <=> parent
-
set_child_purposes <= child_purposes
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Io::Stamp < Core::Io::Base
-
1
set_model_for_input(::Stamp)
-
1
set_json_root(:stamp)
-
-
1
define_attribute_and_json_mapping(%Q{
-
tip_lot <=> tip_lot
-
user <=> user
-
lot <=> lot
-
robot <=> robot
-
-
stamp_details <= stamp_details
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013 Genome Research Ltd.
-
1
class ::Io::StateChange < ::Core::Io::Base
-
1
set_model_for_input(::StateChange)
-
1
set_json_root(:state_change)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
target <=> target
-
contents <=> contents
-
reason <=> reason
-
target_state <=> target_state
-
previous_state => previous_state
-
customer_accepts_responsibility <= customer_accepts_responsibility
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Io::StockMultiplexedLibraryTube < Io::Tube
-
1
set_model_for_input(::StockMultiplexedLibraryTube)
-
1
set_json_root(:tube)
-
-
1
define_attribute_and_json_mapping(%Q{
-
sibling_tubes => sibling_tubes
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013 Genome Research Ltd.
-
1
class Io::Study < Core::Io::Base
-
1
set_model_for_input(::Study)
-
1
set_json_root(:study)
-
1
set_eager_loading { |model| model.include_study_metadata.include_projects }
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
ethically_approved => ethically_approved
-
state => state
-
abbreviation => abbreviation
-
-
study_metadata.study_type.name => type
-
study_metadata.faculty_sponsor.name => sac_sponsor
-
study_metadata.reference_genome.name => reference_genome
-
study_metadata.study_ebi_accession_number => accession_number
-
study_metadata.study_description => description
-
study_metadata.study_abstract => abstract
-
-
study_metadata.contaminated_human_dna => contaminated_human_dna
-
study_metadata.remove_x_and_autosomes? => remove_x_and_autosomes
-
study_metadata.separate_y_chromosome_data => separate_y_chromosome_data
-
study_metadata.contains_human_dna => contains_human_dna
-
study_metadata.commercially_available => commercially_available
-
study_metadata.data_release_study_type.name => data_release_sort_of_study
-
study_metadata.data_release_strategy => data_release_strategy
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
class ::Io::Submission < ::Core::Io::Base
-
1
set_model_for_input(::Submission)
-
1
set_json_root(:submission)
-
1
set_eager_loading { |model| model.include_orders }
-
-
1
define_attribute_and_json_mapping(%Q{
-
state => state
-
orders <=> orders
-
-
user <= user
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ::Io::SubmissionPool < ::Core::Io::Base
-
1
set_model_for_input(::SubmissionPool)
-
1
set_json_root(:submission_pool)
-
1
set_eager_loading { |model| model }
-
-
1
define_attribute_and_json_mapping(%Q{
-
-
plates_in_submission => plates_in_submission
-
used_tag2_layout_templates => used_tag2_layout_templates
-
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ::Io::SubmissionTemplate < ::Core::Io::Base
-
1
set_model_for_input(::SubmissionTemplate)
-
1
set_json_root(:order_template)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Io::Supplier < ::Core::Io::Base
-
# This module adds the behaviour we require from the Supplier module.
-
1
module ApiIoSupport
-
1
def self.included(base)
-
1
base.class_eval do
-
# TODO: add any named scopes
-
# TODO: add any associations
-
end
-
end
-
-
# TODO: add any methods
-
end
-
-
1
set_json_root(:supplier)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
email => email
-
address => address
-
contact_name => contact_name
-
phone_number => phone_number
-
fax => fax
-
supplier_url => url
-
abbreviation => abbreviation
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ::Io::Tag2Layout < ::Core::Io::Base
-
1
set_model_for_input(::Tag2Layout)
-
1
set_json_root(:tag2_layout)
-
1
set_eager_loading { |model| model.include_plate.include_tag }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
plate <=> plate
-
source <=> source
-
-
tag.name => tag.name
-
tag.map_id => tag.identifier
-
tag.oligo => tag.oligo
-
tag.tag_group.name => tag.group
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ::Io::Tag2LayoutTemplate < ::Core::Io::Base
-
1
set_model_for_input(::Tag2LayoutTemplate)
-
1
set_json_root(:tag2_layout_template)
-
1
set_eager_loading { |model| model.include_tag } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
-
tag.name => tag.name
-
tag.map_id => tag.identifier
-
tag.oligo => tag.oligo
-
tag.tag_group.name => tag.group
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::TagGroup < ::Core::Io::Base
-
1
set_model_for_input(::TagGroup)
-
1
set_json_root(:tag_group)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
indexed_tags => tags
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
1
class ::Io::TagLayout < ::Core::Io::Base
-
1
set_model_for_input(::TagLayout)
-
1
set_json_root(:tag_layout)
-
1
set_eager_loading { |model| model.include_plate.include_tag_group }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
plate <=> plate
-
substitutions <=> substitutions
-
tag_group <=> tag_group
-
direction <=> direction
-
walking_by <=> walking_by
-
initial_tag <=> initial_tag
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2015 Genome Research Ltd.
-
1
class ::Io::TagLayoutTemplate < ::Core::Io::Base
-
1
set_model_for_input(::TagLayoutTemplate)
-
1
set_json_root(:tag_layout_template)
-
1
set_eager_loading { |model| model.include_tags } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
tag_group => tag_group
-
direction => direction
-
walking_by => walking_by
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::Transfer < ::Core::Io::Base
-
1
set_model_for_input(::Transfer)
-
1
set_json_root(:transfer)
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::Transfer::BetweenPlates < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::BetweenPlates)
-
1
set_json_root(:transfer)
-
1
set_eager_loading { |model| model.include_source.include_destination }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
destination <=> destination
-
transfers <=> transfers
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::Transfer::BetweenPlatesBySubmission < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::BetweenPlatesBySubmission)
-
1
set_json_root(:transfer)
-
1
set_eager_loading { |model| model.include_source.include_destination }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
destination <=> destination
-
transfers => transfers
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::Transfer::BetweenSpecificTubes < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::BetweenSpecificTubes)
-
1
set_json_root(:transfer)
-
# set_eager_loading { |model| model.include_source.include_destination }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
destination <=> destination
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ::Io::Transfer::BetweenTubesBySubmission < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::BetweenTubesBySubmission)
-
1
set_json_root(:transfer)
-
# set_eager_loading { |model| model.include_source.include_destination }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
destination => destination
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ::Io::Transfer::FromPlateToSpecificTubes < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::FromPlateToSpecificTubes)
-
1
set_json_root(:transfer)
-
1
set_eager_loading { |model| model.include_source.include_transfers }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
targets <= targets
-
transfers => transfers
-
})
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::Transfer::FromPlateToSpecificTubesByPool < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::FromPlateToSpecificTubesByPool)
-
1
set_json_root(:transfer)
-
1
set_eager_loading { |model| model.include_source.include_transfers }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
targets <= targets
-
transfers => transfers
-
})
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::Transfer::FromPlateToTube < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::FromPlateToTube)
-
1
set_json_root(:transfer)
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
destination <=> destination
-
transfers <=> transfers
-
})
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ::Io::Transfer::FromPlateToTubeByMultiplex < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::FromPlateToTubeByMultiplex)
-
1
set_json_root(:transfer)
-
1
set_eager_loading { |model| model.include_source.include_transfers }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
transfers => transfers
-
})
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class ::Io::Transfer::FromPlateToTubeBySubmission < ::Core::Io::Base
-
1
set_model_for_input(::Transfer::FromPlateToTubeBySubmission)
-
1
set_json_root(:transfer)
-
1
set_eager_loading { |model| model.include_source.include_transfers }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
source <=> source
-
transfers => transfers
-
})
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::TransferTemplate < ::Core::Io::Base
-
1
set_model_for_input(::TransferTemplate)
-
1
set_json_root(:transfer_template)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
transfers => transfers
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014 Genome Research Ltd.
-
1
class Io::Tube < Io::Asset
-
1
set_model_for_input(::Tube)
-
1
set_json_root(:tube)
-
1
set_eager_loading { |model| model.include_aliquots.include_scanned_into_lab_event }
-
-
1
define_attribute_and_json_mapping(%Q{
-
state => state
-
purpose.name => purpose.name
-
purpose.uuid => purpose.uuid
-
-
closed => closed
-
concentration.to_f => concentration
-
volume.to_f => volume
-
scanned_in_date => scanned_in_date
-
role => label.prefix
-
purpose.name => label.text
-
-
stock_plate.uuid => stock_plate.uuid
-
stock_plate.barcode => stock_plate.barcode.number
-
stock_plate.barcode_prefix.prefix => stock_plate.barcode.prefix
-
stock_plate.two_dimensional_barcode => stock_plate.barcode.two_dimensional
-
stock_plate.ean13_barcode => stock_plate.barcode.ean13
-
stock_plate.barcode_type => stock_plate.barcode.type
-
-
aliquots => aliquots
-
-
barcode => barcode.number
-
barcode_prefix.prefix => barcode.prefix
-
two_dimensional_barcode => barcode.two_dimensional
-
ean13_barcode => barcode.ean13
-
barcode_type => barcode.type
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Io::Tube::Purpose < Core::Io::Base
-
1
set_model_for_input(::Tube::Purpose)
-
1
set_json_root(:tube_purpose)
-
-
1
define_attribute_and_json_mapping(%Q{
-
name => name
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ::Io::TubeCreation < ::Core::Io::Base
-
1
set_model_for_input(::TubeCreation)
-
1
set_json_root(:tube_creation)
-
1
set_eager_loading { |model| model.include_parent }
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
parent <=> parent
-
child_purpose <=> child_purpose
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class ::Io::TubeFromTubeCreation < ::Core::Io::Base
-
1
set_model_for_input(::TubeFromTubeCreation)
-
1
set_json_root(:tube_from_tube_creation)
-
-
1
define_attribute_and_json_mapping(%Q{
-
user <=> user
-
parent <=> parent
-
child_purpose <=> child_purpose
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class ::Io::User < ::Core::Io::Base
-
1
set_model_for_input(::User)
-
1
set_json_root(:user)
-
1
define_attribute_and_json_mapping(%Q{
-
login => login
-
email <=> email
-
first_name <=> first_name
-
last_name <=> last_name
-
barcode <=> barcode
-
swipecard_code <= swipecard_code
-
swipecard_code? => has_a_swipecard_code
-
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ::Io::Well < ::Core::Io::Base
-
1
set_model_for_input(::Well)
-
1
set_json_root(:well)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
1
define_attribute_and_json_mapping(%Q{
-
state => state
-
map.description => location
-
aliquots => aliquots
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module ModelExtensions::Asset
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_barcode_prefix, -> { includes(:barcode_prefix) }
-
end
-
end
-
-
1
def source_plate
-
self.purpose.source_plate(self)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module ModelExtensions::AssetGroup
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_study, -> { includes(:study) }
-
1
scope :include_assets, -> { includes(:assets) }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module ModelExtensions::BaitLibraryLayout
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ModelExtensions::Plate::NamedScopeHelpers
-
1
include_plate_named_scope :plate
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the BarcodePrinter model.
-
1
module ModelExtensions::BarcodePrinter
-
1
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
module ModelExtensions::Batch
-
1
def self.included(base)
-
1
base.class_eval do
-
# These were in Batch but it makes more sense to keep them here for the moment
-
1
has_many :batch_requests, :include => :request, :inverse_of => :batch
-
1
has_many :requests, :through => :batch_requests, :inverse_of => :batch, :order => 'batch_requests.position ASC, requests.id ASC'
-
-
# This is the new stuff ...
-
1
accepts_nested_attributes_for :requests
-
-
1
scope :include_pipeline, -> { includes( :pipeline => :uuid_object ) }
-
1
scope :include_user, -> { includes(:user) }
-
1
scope :include_requests, -> { includes(
-
:requests => [
-
:uuid_object, :request_metadata, :request_type,
-
{ :submission => :uuid_object },
-
{ :asset => [ :uuid_object, :barcode_prefix, { :aliquots => [ :sample, :tag ] } ] },
-
{ :target_asset => [ :uuid_object, :barcode_prefix, { :aliquots => [ :sample, :tag ] } ] }
-
]
-
)}
-
-
1
after_create :generate_target_assets_for_requests, :if => :need_target_assets_on_requests?
-
1
before_save :manage_downstream_requests
-
end
-
end
-
-
1
def manage_downstream_requests
-
pipeline.manage_downstream_requests(self)
-
end
-
1
private :manage_downstream_requests
-
-
1
def generate_target_assets_for_requests
-
requests_to_update, asset_links = [], []
-
-
asset_type = pipeline.asset_type.constantize
-
requests(:reload).each do |request|
-
# we need to call downstream request before setting the target_asset
-
# otherwise, the request use the target asset to find the next request
-
target_asset = asset_type.create! do |asset|
-
asset.barcode = AssetBarcode.new_barcode unless [ Lane, Well ].include?(asset_type)
-
asset.generate_name(request.asset.name)
-
end
-
-
downstream_requests_needing_asset(request) do |downstream_requests|
-
requests_to_update.concat(downstream_requests.map { |r| [ r.id, target_asset.id ] })
-
end
-
-
request.update_attributes!(:target_asset => target_asset)
-
-
# All links between the two assets as new, so we can bulk create them!
-
asset_links << [request.asset.id, request.target_asset.id]
-
end
-
-
AssetLink::BuilderJob.create(asset_links)
-
-
requests_to_update.each do |request_details|
-
Request.find(request_details.first).update_attributes!(:asset_id => request_details.last)
-
end
-
-
end
-
1
private :generate_target_assets_for_requests
-
-
1
def downstream_requests_needing_asset(request)
-
next_requests_needing_asset = request.next_requests(pipeline).select { |r| r.asset_id.blank? }
-
yield(next_requests_needing_asset) unless next_requests_needing_asset.blank?
-
end
-
-
1
def need_target_assets_on_requests?
-
pipeline.asset_type.present? and pipeline.request_types.detect(&:needs_target_asset?).present?
-
end
-
1
private :need_target_assets_on_requests?
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module ModelExtensions::LibraryTube
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_source_request, -> { includes( :source_request => [ :uuid_object, :request_metadata ] ) }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
module ModelExtensions::Lot
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module ModelExtensions::MultiplexedLibraryTube
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_source_request, -> { includes( :source_request => [ :uuid_object, :request_metadata ] ) }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015,2015 Genome Research Ltd.
-
1
module ModelExtensions::Order
-
1
module Validations
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend DelegateValidation
-
1
delegate_validation :request_options_for_validation, :as => 'request_options', :to => :request_types, :if => :validate_request_options?
-
end
-
end
-
-
# The validation of the request options should happen if we are leaving the building state, or if the
-
# request options have been specified. Once they are specified they are always checked, unless they are
-
# completely blanked.
-
1
def validate_request_options?
-
not building? or not self.request_options.blank?
-
end
-
1
private :validate_request_options?
-
-
1
def request_types_delegate_validator
-
DelegateValidation::CompositeValidator::CompositeValidator(*::RequestType.find(self.request_types.flatten).map(&:delegate_validator))
-
end
-
-
# If this returns true then we check values that have not been set, otherwise we can ignore them. This would
-
# mean that we should not require values that are unset, until we're moving out of the building state.
-
1
def include_unset_values?
-
not building?
-
end
-
-
1
def request_options_for_validation
-
OpenStruct.new({ :owner => self }.reverse_merge(self.request_options || {})).tap do |v|
-
v.class.delegate(:errors, :include_unset_values?, :to => :owner)
-
end
-
end
-
end
-
-
1
def validate_new_record(assets)
-
raise StandardError, 'requested action is not supported on this resource' if not new_record? and asset_group? and assets.present?
-
true
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
include Validations
-
-
1
before_validation :merge_in_structured_request_options
-
-
1
scope :include_study, -> { includes( :study => :uuid_object ) }
-
1
scope :include_project, -> { includes( :project => :uuid_object ) }
-
1
scope :include_assets, -> { includes( :assets => :uuid_object ) }
-
-
1
has_many :submitted_assets
-
1
has_many :assets, :through => :submitted_assets, :before_add => :validate_new_record
-
-
1
scope :that_submitted_asset_id, ->(asset_id) {
-
{ :conditions => { :submitted_assets => { :asset_id => asset_id } }, :joins => :submitted_assets }
-
}
-
-
1
validate :extended_validation
-
1
def extended_validation
-
extended_validators.reduce(true) {|valid,validator| validator.validate_order(self) && valid }
-
end
-
-
# The API can create submissions but we have to prevent someone from changing the study
-
# and the project once they have been set.
-
1
validates_each(:study, :project) do |record, attr, value|
-
# NOTE: This can get called after the record has been saved but before it has been completely saved, i.e. after_create
-
# In this case the original value of the attribute will be nil, so we account for that here.
-
attr_value_was, attr_value_is = record.send(:"#{attr}_id_was"), record.send(:"#{attr}_id")
-
record.errors.add(attr, 'cannot be changed') if not record.new_record? and attr_value_was != attr_value_is and attr_value_was.present?
-
end
-
-
1
def extended_validators
-
ExtendedValidator.for_submission(self)
-
end
-
-
1
extend ClassMethods
-
end
-
end
-
-
1
class NonNilHash
-
1
def initialize(key_style_operation = :symbolize_keys)
-
@key_style_operation = key_style_operation
-
@store = {}
-
end
-
-
1
def deep_merge(hash)
-
@store.deep_merge!(hash.try(@key_style_operation) || {})
-
self
-
end
-
-
1
def [](*keys)
-
node_and_leaf(*keys) { |node, leaf| node.fetch(leaf, nil) }
-
end
-
-
1
def []=(*keys_and_values)
-
value = keys_and_values.pop
-
return if value.nil?
-
node_and_leaf(*keys_and_values) { |node, leaf| node[leaf] = value }
-
end
-
-
1
def fetch(*keys_and_default)
-
default = keys_and_default.pop
-
node_and_leaf(*keys_and_default) { |node, left| node.fetch(left, default) }
-
end
-
-
1
def to_hash
-
Hash.new.deep_merge(@store)
-
end
-
-
1
def node_and_leaf(*keys, &block)
-
leaf = keys.pop
-
node = keys.inject(@store) { |h,k| h[k] ||= {} }
-
yield(node, leaf)
-
end
-
1
private :node_and_leaf
-
end
-
-
1
def request_type_multiplier(&block)
-
yield(request_types.last.to_s.to_sym) unless request_types.blank?
-
end
-
-
1
def request_options_structured
-
NonNilHash.new(:stringify_keys).tap do |json|
-
NonNilHash.new.deep_merge(self.request_options).tap do |attributes|
-
json['read_length'] = attributes[:read_length].try(:to_i)
-
json['library_type'] = attributes[:library_type]
-
json['fragment_size_required', 'from'] = attributes[:fragment_size_required_from].try(:to_i)
-
json['fragment_size_required', 'to'] = attributes[:fragment_size_required_to].try(:to_i)
-
json['bait_library'] = attributes[:bait_library_name]
-
json['sequencing_type'] = attributes[:sequencing_type]
-
json['insert_size'] = attributes[:insert_size].try(:to_i)
-
request_type_multiplier { |id| json['number_of_lanes'] = attributes[:multiplier, id] }
-
end
-
end.to_hash
-
end
-
-
1
def request_options_structured=(values)
-
@request_options_structured = NonNilHash.new.tap do |attributes|
-
NonNilHash.new(:stringify_keys).deep_merge(values).tap do |json|
-
# NOTE: Be careful with the names here to ensure that they match up, exactly with what is in a template.
-
# If the template uses symbol names then these need to be symbols too.
-
attributes[:read_length] = json['read_length']
-
attributes['library_type'] = json['library_type']
-
attributes['fragment_size_required_from'] = json['fragment_size_required', 'from']
-
attributes['fragment_size_required_to'] = json['fragment_size_required', 'to']
-
attributes[:bait_library_name] = json['bait_library']
-
attributes[:sequencing_type] = json['sequencing_type']
-
attributes[:insert_size] = json['insert_size']
-
request_type_multiplier { |id| attributes[:multiplier, id] = json['number_of_lanes'] }
-
end
-
end.to_hash
-
end
-
-
1
def merge_in_structured_request_options
-
self.request_options ||= {}
-
self.request_options = self.request_options.deep_merge(@request_options_structured || {})
-
true
-
end
-
1
private :merge_in_structured_request_options
-
-
1
def request_type_objects
-
return [] if self.request_types.blank?
-
::RequestType.find(self.request_types)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the PacBioLibraryTube model.
-
1
module ModelExtensions::PacBioLibraryTube
-
1
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module ModelExtensions::Pipeline
-
# If you need to do something to a batch based on updates it has received then this is the place to
-
# do it. The batch will be passed in and, really, you should call back into it to get the right
-
# thing done.
-
1
def manage_downstream_requests(batch)
-
# By default nothing is done here!
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015,2015 Genome Research Ltd.
-
module ModelExtensions::Plate
-
module NamedScopeHelpers
-
def include_plate_named_scope(plate_association)
-
11
scope :"include_#{plate_association}", -> { includes(plate_association.to_sym => ::ModelExtensions::Plate::PLATE_INCLUDES) }
-
end
-
end
-
-
PLATE_INCLUDES = [
-
:plate_metadata, {
-
:wells => [
-
:map,
-
:transfer_requests_as_target,
-
:uuid_object
-
]
-
}
-
]
-
-
def self.included(base)
-
base.class_eval do
-
1
scope :include_plate_purpose, -> { includes(:plate_purpose) }
-
scope :include_plate_metadata, -> { includes(:plate_metadata) }
-
delegate :pool_id_for_well, :to => :plate_purpose, :allow_nil => true
-
end
-
end
-
-
def plate_purpose_or_stock_plate
-
self.plate_purpose || PlatePurpose.find_by_name('Stock Plate')
-
end
-
-
def source_plate
-
self.plate_purpose.source_plate(self)
-
end
-
-
def source_plates
-
self.plate_purpose.source_plates(self)
-
end
-
-
def library_source_plate
-
self.plate_purpose.library_source_plate(self)
-
end
-
-
def library_source_plates
-
self.plate_purpose.library_source_plate(self)
-
end
-
-
# Returns a hash from the submission for the pools to the wells that form that pool on this plate. This is
-
# not necessarily efficient but it is correct. Unpooled wells, those without submissions, are completely
-
# ignored within the returned result.
-
def pools
-
ActiveSupport::OrderedHash.new.tap do |pools|
-
Request.include_request_metadata.for_pooling_of(self).each do |request|
-
pools[request.pool_id] = { :wells => request.pool_into.split(',') }.tap do |pool_information|
-
request.update_pool_information(pool_information)
-
end unless request.pool_id.nil?
-
end
-
end
-
end
-
-
# Adds pre-capture pooling information, we need to delegate this to the stock plate, as we need all the wells
-
def pre_cap_groups
-
ActiveSupport::OrderedHash.new.tap do |groups|
-
Request.include_request_metadata.for_pre_cap_grouping_of(self).each do |request|
-
groups[request.group_id] = { :wells => request.group_into.split(',') }.tap do |pool_information|
-
pool_information[:pre_capture_plex_level] ||= request.request_metadata.pre_capture_plex_level
-
# We supply the submission id to assist with correctly tagging transfer requests later
-
pool_information[:submission_id] ||= request.submission_id
-
end unless request.group_id.nil?
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module ModelExtensions::Project
-
1
def self.included(base)
-
1
base.class_eval do
-
1
has_many :submissions
-
1
scope :include_roles, -> { includes( :roles => :users ) }
-
end
-
end
-
-
1
def roles_as_json
-
Hash[
-
self.roles.map do |role|
-
[ role.name.underscore, role.users.map { |user| { :login => user.login, :email => user.email, :name => user.name } } ]
-
end
-
]
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module ModelExtensions::Request
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_source_asset, -> { includes(
-
:asset => [
-
:uuid_object,
-
:barcode_prefix,
-
:scanned_into_lab_event,
-
{ :aliquots => [ :sample, :tag ] }
-
]
-
)}
-
1
scope :include_target_asset, -> { includes(
-
:target_asset => [
-
:uuid_object,
-
:barcode_prefix,
-
{ :aliquots => [ :sample, :tag ] }
-
]
-
)}
-
-
1
scope :include_study, -> { includes( :study => :uuid_object ) }
-
1
scope :include_project, -> { includes( :project => :uuid_object ) }
-
1
scope :include_request_type, -> { includes( :request_type ) }
-
1
scope :include_submission, -> { includes( :submission => :uuid_object ) }
-
-
# The assets on a request can be treated as a particular class when being used by certain pieces of code. For instance,
-
# QC might be performed on a source asset that is a well, in which case we'd like to load it as such.
-
1
belongs_to :target_asset, :class_name => 'Aliquot::Receptacle', :inverse_of => :requests_as_target
-
1
accepts_nested_attributes_for :target_asset, :update_only => true
-
-
1
belongs_to :asset, :class_name => 'Aliquot::Receptacle', :inverse_of => :requests
-
1
accepts_nested_attributes_for :asset, :update_only => true
-
1
belongs_to :source_well, :class_name => 'Well', :foreign_key => :asset_id
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
module ModelExtensions::Robot
-
-
1
def json_for_properties
-
Hash[robot_properties.map {|prop| [prop.key,prop.value] }]
-
end
-
-
1
private
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module ModelExtensions::Sample
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_studies, -> { includes(:studies => :study_metadata) }
-
-
1
has_one :primary_study_samples, :class_name => 'StudySample', :order => 'study_id'
-
1
has_one :primary_study, :through => :primary_study_samples, :source => :study
-
end
-
end
-
-
1
def sample_reference_genome_name
-
sample_reference_genome.try(:name)
-
end
-
-
1
def sample_reference_genome_name=(name)
-
sample_metadata.reference_genome = ReferenceGenome.find_by_name(name)
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
module ModelExtensions::SampleManifest
-
def self.included(base)
-
base.class_eval do
-
scope :include_samples, -> { includes(
-
:samples => [
-
:uuid_object, {
-
:sample_metadata => :reference_genome,
-
:primary_study => { :study_metadata => :reference_genome }
-
}
-
]
-
1
) }
-
delegate :io_samples, :to => :core_behaviour
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module ModelExtensions::SampleTube
-
1
def self.included(base)
-
1
base.class_eval do
-
1
has_many :library_tubes, :through => :links_as_parent, :source => :descendant
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
module ModelExtensions::Stamp
-
-
1
def stamp_details=(details)
-
self.stamp_qcables.build(details.map{|d| locate_qcable(d) })
-
end
-
-
1
private
-
-
1
def locate_qcable(d)
-
d['qcable'] = Uuid.find_by_external_id(d['qcable']).resource
-
d
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the StateChange model.
-
1
module ModelExtensions::StateChange
-
1
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module ModelExtensions::Study
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_samples, -> { includes(:samples) }
-
1
scope :include_projects, -> { includes(:projects) }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module ModelExtensions::Submission
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_orders, -> { includes( :orders => { :study => :uuid_object, :project => :uuid_object, :assets => :uuid_object } ) }
-
-
1
def order
-
orders.first
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module ModelExtensions::TagLayout
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ModelExtensions::Plate::NamedScopeHelpers
-
1
include_plate_named_scope :plate
-
-
1
scope :include_tag_group, -> { includes(:tag_group => :tags) }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the TagLayoutTemplate model.
-
1
module ModelExtensions::TagLayoutTemplate
-
1
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the Transfer model.
-
1
module ModelExtensions::Transfer
-
1
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the TransferTemplate model.
-
1
module ModelExtensions::TransferTemplate
-
1
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
module ModelExtensions::Tube
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :include_purpose, -> { includes(:purpose) }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
module ModelExtensions::Well
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :for_api_plate_json, -> { includes(
-
:map,
-
:transfer_requests, # Should be :transfer_requests_as_target
-
:uuid_object, {
-
:plate => :uuid_object,
-
:aliquots => [
-
:bait_library, {
-
:tag => :tag_group,
-
:sample => [
-
:uuid_object, {
-
:primary_study => { :study_metadata => :reference_genome },
-
:sample_metadata => :reference_genome
-
}
-
]
-
}
-
]
-
}
-
)
-
}
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Admin::BaitLibraries::BaitLibrarySuppliersController < ApplicationController
-
1
before_filter :admin_login_required
-
1
before_filter :discover_bait_library_supplier, :only => [:edit, :update, :destroy]
-
1
def new
-
@bait_library_supplier = BaitLibrary::Supplier.new
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@bait_library_supplier = BaitLibrary::Supplier.new(params[:bait_library_supplier])
-
-
respond_to do |format|
-
if @bait_library_supplier.save
-
flash[:notice] = 'Supplier was successfully created.'
-
format.html { redirect_to(admin_bait_libraries_path) }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @bait_library_supplier.update_attributes(params[:bait_library_supplier])
-
flash[:notice] = 'Supplier was successfully updated.'
-
format.html { redirect_to(admin_bait_libraries_path) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
1
def destroy
-
if @bait_library_supplier.bait_libraries.visible.count > 0
-
respond_to do |format|
-
flash[:error] = "Can not delete '#{@bait_library_supplier.name}', supplier is in use by #{@bait_library_supplier.bait_libraries.visible.count} libraries.<br/>"
-
format.html { redirect_to(admin_bait_libraries_path) }
-
end
-
else
-
respond_to do |format|
-
if @bait_library_supplier.hide
-
flash[:notice] = 'Supplier was successfully deleted.'
-
end
-
format.html { redirect_to(admin_bait_libraries_path) }
-
end
-
end
-
end
-
1
private
-
1
def discover_bait_library_supplier
-
@bait_library_supplier = BaitLibrary::Supplier.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Admin::BaitLibraries::BaitLibraryTypesController < ApplicationController
-
1
before_filter :admin_login_required
-
1
before_filter :discover_bait_library_type, :only => [:edit, :update, :destroy]
-
1
def new
-
@bait_library_type = BaitLibraryType.new
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@bait_library_type = BaitLibraryType.new(params[:bait_library_type])
-
-
respond_to do |format|
-
if @bait_library_type.save
-
flash[:notice] = 'Bait Library Type was successfully created.'
-
format.html { redirect_to(admin_bait_libraries_path) }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @bait_library_type.update_attributes(params[:bait_library_type])
-
flash[:notice] = 'Bait Library Type was successfully updated.'
-
format.html { redirect_to(admin_bait_libraries_path) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
1
def destroy
-
if @bait_library_type.bait_libraries.visible.count > 0
-
respond_to do |format|
-
flash[:error] = "Can not delete '#{@bait_library_type.name}', bait library type is in use by #{@bait_library_type.bait_libraries.visible.count} libraries."
-
format.html { redirect_to(admin_bait_libraries_path) }
-
end
-
else
-
respond_to do |format|
-
if @bait_library_type.hide
-
flash[:notice] = 'Bait Library Type was successfully deleted.'
-
end
-
format.html { redirect_to(admin_bait_libraries_path) }
-
end
-
end
-
end
-
1
private
-
1
def discover_bait_library_type
-
@bait_library_type = BaitLibraryType.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
class Admin::BaitLibrariesController < ApplicationController
-
1
before_filter :admin_login_required
-
1
before_filter :discover_bait_library, :only => [:edit, :update, :destroy]
-
-
1
def index
-
@bait_libraries = BaitLibrary.visible
-
@bait_library_types = BaitLibraryType.visible
-
@bait_library_suppliers = BaitLibrary::Supplier.visible
-
end
-
-
1
def new
-
@bait_library = BaitLibrary.new
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@bait_library = BaitLibrary.new(params[:bait_library])
-
-
respond_to do |format|
-
if @bait_library.save
-
flash[:notice] = 'Bait Library was successfully created.'
-
format.html { redirect_to(admin_bait_libraries_path) }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @bait_library.update_attributes(params[:bait_library])
-
flash[:notice] = 'Bait Library was successfully updated.'
-
format.html { redirect_to(admin_bait_libraries_path) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
1
def destroy
-
respond_to do |format|
-
if @bait_library.hide
-
flash[:notice] = 'Bait Library was successfully deleted.'
-
end
-
format.html { redirect_to(admin_bait_libraries_path) }
-
end
-
end
-
-
1
private
-
1
def discover_bait_library
-
@bait_library = BaitLibrary.find(params[:id])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::CustomTextsController < ApplicationController
-
1
before_filter :admin_login_required
-
-
1
def index
-
@custom_texts = CustomText.find :all
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @custom_texts.to_xml }
-
end
-
end
-
-
1
def show
-
@custom_text = CustomText.find(params[:id])
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @custom_text.to_xml }
-
end
-
end
-
-
1
def new
-
@custom_text = CustomText.new
-
end
-
-
1
def create
-
@custom_text = CustomText.new(params[:custom_text])
-
respond_to do |format|
-
if @custom_text.save
-
flash[:notice] = "Custom text successfully created"
-
format.html { redirect_to admin_custom_text_path(@custom_text) }
-
else
-
flash[:error] = "Problems creating your new custom text"
-
format.html { render :action => :new }
-
end
-
end
-
end
-
-
1
def edit
-
@custom_text = CustomText.find(params[:id])
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def update
-
@custom_text = CustomText.find(params[:id])
-
if @custom_text.update_attributes(params[:custom_text])
-
flash[:notice] = "Details have been updated"
-
redirect_to admin_custom_text_path(@custom_text)
-
else
-
flash[:error] = "Failed to update attributes"
-
render :action => "edit", :id => @custom_text.id
-
end
-
end
-
-
1
def destroy
-
custom_text = CustomText.find(params[:id])
-
if custom_text.destroy
-
flash[:notice] = "Custom text deleted"
-
else
-
flash[:notice] = "Failed to destroy custom text"
-
end
-
redirect_to admin_custom_texts_url
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::DelayedJobsController < ApplicationController
-
1
before_filter :admin_login_required
-
-
1
def index
-
@jobs = Delayed::Job.all
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::FacultySponsorsController < ApplicationController
-
1
before_filter :admin_login_required
-
1
before_filter :discover_faculty_sponsor, :only => [:show, :edit, :update, :destroy]
-
-
1
def index
-
@faculty_sponsors = FacultySponsor.all
-
end
-
-
1
def show
-
end
-
-
1
def new
-
@faculty_sponsor = FacultySponsor.new
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@faculty_sponsor = FacultySponsor.new(params[:faculty_sponsor])
-
-
respond_to do |format|
-
if @faculty_sponsor.save
-
flash[:notice] = 'Faculty Sponsor was successfully created.'
-
format.html { redirect_to(admin_faculty_sponsors_path) }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @faculty_sponsor.update_attributes(params[:faculty_sponsor])
-
flash[:notice] = 'Faculty Sponsor was successfully updated.'
-
format.html { redirect_to(admin_faculty_sponsors_path) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
1
def destroy
-
@faculty_sponsor.destroy
-
-
respond_to do |format|
-
flash[:notice] = 'Faculty Sponsor was successfully deleted.'
-
format.html { redirect_to(admin_faculty_sponsors_path) }
-
end
-
end
-
-
1
private
-
1
def discover_faculty_sponsor
-
@faculty_sponsor = FacultySponsor.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::PlatePurposesController < ApplicationController
-
1
before_filter :admin_login_required
-
1
before_filter :discover_plate_purpose, :only => [:show, :edit, :update, :destroy]
-
-
1
def index
-
plate_purposes = PlatePurpose.find(:all)
-
@plate_purposes = plate_purposes.map{ |purpose| purpose.becomes(PlatePurpose) }
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @plate_purposes }
-
end
-
end
-
-
1
def show
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @plate_purpose }
-
end
-
end
-
-
1
def new
-
@plate_purpose = PlatePurpose.new
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @plate_purpose }
-
end
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@plate_purpose = PlatePurpose.new(params[:plate_purpose])
-
-
respond_to do |format|
-
if @plate_purpose.save
-
flash[:notice] = 'Plate Purpose was successfully created.'
-
format.html { redirect_to(admin_plate_purposes_path) }
-
format.xml { render :xml => @plate_purpose, :status => :created, :location => @plate_purpose }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @plate_purpose.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @plate_purpose.update_attributes(params[:plate_purpose])
-
flash[:notice] = 'Plate Purpose was successfully updated.'
-
format.html { redirect_to(admin_plate_purposes_path) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @plate_purpose.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@plate_purpose.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(admin_plate_purposes_url) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
private
-
1
def discover_plate_purpose
-
@plate_purpose = PlatePurpose.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::RobotPropertiesController < ApplicationController
-
1
before_filter :find_robot_by_id
-
-
1
def index
-
@robot_properties = @robot.robot_properties
-
end
-
-
1
def show
-
@robot_property = @robot.robot_properties.find(params[:id])
-
end
-
-
1
def new
-
@robot_property = @robot.robot_properties.build
-
end
-
-
1
def create
-
@robot_property = @robot.robot_properties.build(params[:robot_property])
-
if @robot_property.save
-
redirect_to [:admin, @robot, @robot_property]
-
else
-
render :action => "new"
-
end
-
end
-
-
1
def edit
-
@robot_property = @robot.robot_properties.find(params[:id])
-
end
-
-
1
def update
-
@robot_property = RobotProperty.find(params[:id])
-
if @robot_property.update_attributes(params[:robot_property])
-
redirect_to [:admin, @robot, @robot_property]
-
else
-
render :action => "edit"
-
end
-
end
-
-
1
def destroy
-
@robot_property = RobotProperty.find(params[:id])
-
@robot_property.destroy
-
respond_to do |format|
-
format.html { redirect_to admin_robot_robot_properties_path(@robot) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def find_robot_by_id
-
@robot = Robot.find(params[:robot_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::RobotsController < ApplicationController
-
1
before_filter :find_robot_by_id, :only => [:show, :edit, :update, :destroy]
-
-
1
def index
-
@robots = Robot.find(:all)
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @robots }
-
end
-
end
-
-
1
def show
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @robot }
-
end
-
end
-
-
1
def new
-
@robot = Robot.new
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @robot }
-
end
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@robot = Robot.new(params[:robot])
-
-
respond_to do |format|
-
if @robot.save
-
flash[:notice] = 'Robot was successfully created.'
-
format.html { redirect_to admin_robot_path(@robot) }
-
format.xml { render :xml => @robot, :status => :created, :location => @robot }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @robot.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @robot.update_attributes(params[:robot])
-
flash[:notice] = 'Robot was successfully updated.'
-
format.html { redirect_to admin_robot_path(@robot) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @robot.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@robot.destroy
-
flash[:notice] = "Robot removed successfully"
-
-
respond_to do |format|
-
format.html { redirect_to(admin_robots_url) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def find_robot_by_id
-
@robot = Robot.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Admin::StudiesController < ApplicationController
-
1
before_filter :admin_login_required
-
-
1
def index
-
@studies = Study.all(:order => "name ASC")
-
end
-
-
1
def show
-
@study = Study.find(params[:id])
-
flash.now[:warning] = @study.warnings if @study.warnings.present?
-
end
-
-
1
def update
-
@study = Study.find(params[:id])
-
flash.now[:warning] = @study.warnings if @study.warnings.present?
-
flash[:notice] = "Your study has been updated"
-
render :partial => "manage_single_study"
-
end
-
-
1
def edit
-
@request_types = RequestType.all(:order => "name ASC")
-
if params[:id] != "0"
-
@study = Study.find(params[:id])
-
flash.now[:warning] = @study.warnings if @study.warnings.present?
-
render :partial => "edit", :locals => { :study => @study }
-
else
-
render :nothing => true
-
end
-
end
-
-
# TODO: remove unneeded code
-
1
def filter
-
unless params[:filter].nil?
-
if params[:filter][:by] == "not approved"
-
filter_conditions = {:approved => false}
-
end
-
end
-
-
if params[:filter][:by] == "not approved" || params[:filter][:by] == "all"
-
@studies = Study.find(:all, :conditions => filter_conditions, :order => :name ).select { |p| p.name.include? params[:q] }
-
end
-
-
unless params[:filter].nil?
-
if params[:filter][:by] == "unallocated manager"
-
@studies = Study.all.select { |p| p.name.include?(params[:q]) && !(p.roles.map { |r| r.name }.include?('manager')) }
-
end
-
end
-
-
case params[:filter][:status]
-
when "open"
-
@studies = @studies.select { |p| p.active? }
-
when "closed"
-
@studies = @studies.reject { |p| p.active? }
-
end
-
@request_types = RequestType.all.sort_by{|r| r.name}
-
render :partial => "filtered_studies"
-
end
-
-
-
1
def managed_update
-
@study = Study.find(params[:id])
-
redirect_if_not_owner_or_admin(@study)
-
-
Document.create!(:documentable => @study, :uploaded_data => params[:study][:uploaded_data]) unless params[:study][:uploaded_data].blank?
-
params[:study].delete(:uploaded_data)
-
-
ActiveRecord::Base.transaction do
-
@study.update_attributes!(params[:study])
-
flash[:notice] = "Your study has been updated"
-
redirect_to :controller => "admin/studies", :action => "update", :id => @study.id
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
logger.warn "Failed to update attributes: #{@study.errors.map {|e| e.to_s }}"
-
flash[:error] = "Failed to update attributes for study!"
-
render :action => :show, :id => @study.id and return
-
end
-
-
1
def sort
-
@studies = Study.find(:all).sort_by { |study| study.name }
-
if params[:sort] == "date"
-
@studies = @studies.sort_by { |study| study.created_at}
-
elsif params[:sort] == "owner"
-
@studies = @studies.sort_by { |study| study.user_id }
-
end
-
render :partial => "studies"
-
end
-
-
1
private
-
-
1
def redirect_if_not_owner_or_admin(study)
-
unless current_user.owner?(study) or current_user.is_administrator?
-
flash[:error] = "Study details can only be altered by the owner (#{study.user.login}) or an administrator"
-
redirect_to study_path(study)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Admin::UsersController < ApplicationController
-
1
before_filter :admin_login_required
-
1
before_filter :setup_user, :only => [:edit, :show, :grant_user_role, :remove_user_role]
-
-
1
def index
-
@users = User.all(:order => "login ASC")
-
end
-
-
1
def edit
-
@user_roles = @user.roles.select{|r| r.name == "administrator" || r.name == "manager" || r.name == "internal"}
-
@all_roles = Role.all(:select => "distinct `name`")
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
@studies = Study.all(:order => :id)
-
@projects = Project.all(:order => :id)
-
-
respond_to do |format|
-
format.js
-
format.html
-
end
-
end
-
-
1
def show
-
end
-
-
1
def switch
-
session[:user] = params[:id]
-
redirect_to studies_url
-
end
-
-
1
def update
-
@user = User.find(params[:id])
-
-
Role.general_roles.each do |role|
-
if params[:role] && params[:role][role.name]
-
@user.has_role(role.name)
-
else
-
@user.has_no_role(role.name)
-
end
-
end
-
-
if @user.id == params[:id].to_i
-
@user.update_attributes(params[:user])
-
end
-
if @user.save
-
flash[:notice] = "Profile updated"
-
else
-
flash[:error] = "Problem updating profile"
-
end
-
redirect_to profile_path(@user)
-
end
-
-
1
def grant_user_role
-
if request.xhr?
-
if params[:role]
-
if params[:role][:authorizable_type] == "Project"
-
authorizable_object = Project.find(params[:role][:authorizable_id])
-
else
-
authorizable_object = Study.find(params[:role][:authorizable_id])
-
end
-
@user.has_role(params[:role][:authorizable_name].to_s, authorizable_object)
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
-
flash[:notice] = "Role added"
-
render :partial => "roles", :status => 200
-
else
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
flash[:error] = "A problem occurred while adding the role"
-
render :partial => "roles", :status => 500
-
end
-
else
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
flash[:error] = "A problem occurred while adding the role"
-
render :partial => "roles", :status => 401
-
end
-
end
-
-
1
def remove_user_role
-
if request.xhr?
-
if params[:role]
-
if params[:role][:authorizable_type] == "project"
-
authorizable_object = Project.find(params[:role][:authorizable_id])
-
else
-
authorizable_object = Study.find(params[:role][:authorizable_id])
-
end
-
@user.has_no_role(params[:role][:authorizable_name].to_s, authorizable_object)
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
-
flash[:error] = "Role was removed"
-
render :partial => "roles", :status => 200
-
else
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
flash[:error] = "A problem occurred while removing the role"
-
render :partial => "roles", :status => 500
-
end
-
else
-
@users_roles = @user.study_and_project_roles.sort_by(&:name)
-
flash[:error] = "A problem occurred while removing the role"
-
render :partial => "roles", :status => 401
-
end
-
end
-
-
1
def filter
-
if params[:q]
-
@users = User.all(:order => "login ASC" ).select{ |p| p.name.downcase.include?(params[:q].downcase) || p.login.downcase.include?(params[:q].downcase) }
-
end
-
-
render :partial => "users", :locals => {:users => @users}
-
end
-
-
1
private
-
1
def setup_user
-
@user = User.find(params[:id], :include => [:roles])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class AdminController < ApplicationController
-
-
1
before_filter :admin_login_required
-
-
1
def index
-
end
-
-
1
def filter
-
if params[:q].blank?
-
@users = User.all
-
else
-
@users = User.find_all_by_login(params[:q])
-
end
-
render :partial => 'admin/users/users', :locals => { :users => @users }
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class Api::AliquotsController < Api::BaseController
-
1
self.model_class = Aliquot
-
-
1
before_filter :prepare_object, :only => [ :show, :update, :destroy ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
def prepare_list_context
-
@context, @context_options = ::Aliquot.including_associations_for_json, { :order => 'updated_at DESC' }
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::AssetAuditsController < Api::BaseController
-
1
self.model_class = AssetAudit
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context, @context_options = ::AssetAudit.including_associations_for_json, { :order => 'id DESC' }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::AssetLinksController < Api::BaseController
-
1
self.model_class = AssetLink
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
def prepare_list_context
-
@context, @context_options = ::AssetLink.including_associations_for_json, { :order => 'id DESC' }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::AssetsController < Api::BaseController
-
1
def children
-
respond_to do |format|
-
format.json { render :json => @object.children.map(&:list_json) }
-
end
-
end
-
-
1
def parents
-
respond_to do |format|
-
format.json { render :json => @object.parents.map(&:list_json) }
-
end
-
end
-
-
1
def holder_quarantine
-
# should holder be exposed in the API ?
-
# rather than location and container
-
respond_to do |format|
-
format.json { render :json => @object.holder}
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
class Api::BaseController < ApplicationController
-
class_attribute :model_class
-
before_filter { |controller| Uuid.translate_uuids_to_ids_in_params(controller.params) }
-
around_filter :wrap_in_transaction, :only => [ :create, :update, :destroy ]
-
1
-
delegate :render_class, :to => :model_class
-
-
#--
-
# Exception handling code
-
#++
-
1
rescue_from ActiveRecord::RecordInvalid do |exception|
-
errors = exception.record.errors.inject(Hash.new { |h,k| h[k] = [] }) do |hash, field_and_error_pair|
-
hash.tap { hash[ field_and_error_pair.first ].push(field_and_error_pair.last) }
-
end
-
respond_to do |format|
-
format.json { render :json => self.render_class.map_attribute_to_json_attribute_in_errors(errors), :status => :unprocessable_entity }
-
end
-
end
-
-
1
rescue_from ActiveRecord::RecordNotFound do |exception|
-
head(:not_found)
-
end
-
-
#--
-
# CRUD methods
-
#++
-
1
def show
-
respond_to do |format|
-
format.json { render :json => @object.to_json }
-
end
-
end
-
-
1
def index
-
options = { :order => 'id DESC' }.merge(@context_options || {})
-
-
results = @context.paginate({ :page => params[:page] || 1, :total_entries => 100000000, :per_page => 500 }.merge(options))
-
-
respond_to do |format|
-
format.json { render :json => results }
-
end
-
end
-
-
1
def create
-
object = self.render_class.create!(params)
-
respond_to do |format|
-
format.json { render :json => object.to_json, :status => :created, :location => object }
-
end
-
end
-
-
1
def update
-
self.render_class.update_attributes!(@object, params)
-
respond_to do |format|
-
format.json { head :ok }
-
end
-
end
-
-
1
def destroy
-
@object.destroy
-
-
respond_to do |format|
-
format.json { head :ok }
-
end
-
end
-
-
1
private
-
-
#--
-
# Preparation filters for CRUD methods.
-
#++
-
1
def prepare_object
-
@object = self.model_class.find(params[:id])
-
end
-
-
1
def prepare_list_context
-
@context = self.model_class
-
end
-
-
1
def wrap_in_transaction(&block)
-
ActiveRecord::Base.transaction(&block)
-
end
-
-
1
def attributes_for_model_from_parameters
-
params[self.model_class.name.underscore]
-
end
-
-
1
def uuid_to_id
-
Uuid.translate_uuids_to_ids_in_params(params)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::BatchRequestsController < Api::BaseController
-
1
self.model_class = BatchRequest
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context, @context_options = ::BatchRequest.including_associations_for_json, { :order => 'id DESC' }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::BatchesController < Api::BaseController
-
1
self.model_class = Batch
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Batch.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::EventsController < Api::BaseController
-
1
self.model_class = Event
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context, @context_options = ::Event.including_associations_for_json, { :order => 'id DESC' }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::LanesController < Api::AssetsController
-
1
self.model_class = Lane
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Lane.including_associations_for_json
-
@context = ::LibraryTube.find(params[:library_tube_id]).children unless params[:library_tube_id].blank?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::LibraryTubesController < Api::AssetsController
-
1
self.model_class = LibraryTube
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::LibraryTube.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Api::MultiplexedLibraryTubesController < Api::AssetsController
-
1
self.model_class = MultiplexedLibraryTube
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::MultiplexedLibraryTube.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class Api::OrdersController < Api::BaseController
-
1
self.model_class = Order
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [:index ]
-
-
1
private
-
-
1
def prepare_list_context
-
case
-
when params[:submission_id].present?
-
@context = ::Submission.find(params[:submission_id]).orders
-
else
-
@context = ::Order.including_associations_for_json
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::PlatePurposesController < Api::BaseController
-
1
self.model_class = PlatePurpose
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Api::PlatesController < Api::AssetsController
-
1
self.model_class = Plate
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Plate.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::ProjectsController < Api::BaseController
-
1
self.model_class = Project
-
-
1
before_filter :prepare_object, :only => [ :show, :update, :destroy ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Project
-
@context = ::Study.find(params[:study_id]).projects unless params[:study_id].blank?
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Api::PulldownMultiplexedLibraryTubesController < Api::AssetsController
-
1
self.model_class = PulldownMultiplexedLibraryTube
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::PulldownMultiplexedLibraryTube.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::RequestsController < Api::BaseController
-
1
self.model_class = Request
-
-
1
before_filter :prepare_object, :only => [ :show, :update, :destroy ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
if not params[:sample_tube_id].blank?
-
@context = ::SampleTube.find(params[:sample_tube_id]).requests
-
elsif not params[:library_tube_id].blank?
-
@context = ::LibraryTube.find(params[:library_tube_id]).requests
-
else
-
@context = Request.including_associations_for_json
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::SampleTubesController < Api::AssetsController
-
1
self.model_class = SampleTube
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::SampleTube.including_associations_for_json
-
@context = ::SampleTube.with_sample_id(params[:sample_id]) unless params[:sample_id].nil?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Api::SamplesController < Api::BaseController
-
1
self.model_class = Sample
-
-
1
before_filter :prepare_object, :only => [ :show, :update, :destroy ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
def next_sanger_sample_id
-
respond_to do |format|
-
format.json { render :json => SangerSampleId.create().id }
-
end
-
end
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Sample.including_associations_for_json
-
@context = ::Study.find(params[:study_id]).samples unless params[:study_id].blank?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::StudiesController < Api::BaseController
-
1
self.model_class = ::Study
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Study.including_associations_for_json
-
@context = ::Project.find(params[:project_id]).studies unless params[:project_id].blank?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::StudySamplesController < Api::BaseController
-
1
self.model_class = StudySample
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context, @context_options = ::StudySample.including_associations_for_json, { :order => 'id DESC' }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Api::SubmissionsController < Api::BaseController
-
1
self.model_class = Submission
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Submission.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::TagsController < Api::BaseController
-
1
self.model_class = Tag
-
-
1
before_filter :prepare_object, :only => [ :show ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Tag.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Api::WellsController < Api::AssetsController
-
1
self.model_class = Well
-
-
1
before_filter :prepare_object, :only => [ :show, :children, :parents ]
-
1
before_filter :prepare_list_context, :only => [ :index ]
-
-
1
private
-
-
1
def prepare_list_context
-
@context = ::Well.including_associations_for_json
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014 Genome Research Ltd.
-
1
require "exceptions"
-
-
# Filters added to this controller apply to all controllers in the application.
-
# Likewise, all the methods added will be available for all controllers.
-
-
1
class ApplicationController < ActionController::Base
-
1
helper :all # include all helpers, all the time
-
-
# See ActionController::RequestForgeryProtection for details
-
# Uncomment the :secret if you're not using the cookie session store
-
1
protect_from_forgery # :secret => "4418f0a814148fc28a0a38971e433b7d"
-
-
# See ActionController::Base for details
-
# Uncomment this to filter the contents of submitted sensitive data parameters
-
# from your application log (in this case, all fields with names like "password").
-
# filter_parameter_logging :password
-
-
# Provide authentication, and "remember me"
-
1
include AuthenticatedSystem
-
1
before_filter :login_required
-
1
before_filter :extract_header_info
-
-
#clean parameters hash from :check generated by checktext_field fields.
-
1
def clean_params_from_check(object)
-
return object unless object.is_a?(Hash)
-
new_hash = object.class.new
-
to_nil = []
-
object.each do |k, v|
-
if k == "check" and v.is_a?(Hash)
-
v.each do |k, v|
-
to_nil << k unless v == "true"
-
end
-
else # normal
-
new_hash[k] = clean_params_from_check(v)
-
end
-
end
-
-
to_nil.each do |k|
-
new_hash[k] = nil
-
end
-
new_hash
-
-
end
-
-
1
public
-
-
1
def block_api_access(message = nil, format = :xml)
-
content = {:error => "Unsupported API access"}
-
content[:message] = message unless message.nil?
-
{ format => content.send("to_#{format}".to_sym, :root => :errors), :status => 406 }
-
end
-
-
1
def extract_header_info
-
1
exclude_nested_resource = request.headers["HTTP_EXCLUDE_NESTED_RESOURCE"] || params[:exclude_nested_resource]
-
1
@exclude_nested_resource = exclude_nested_resource && exclude_nested_resource.to_s.downcase == "true"
-
end
-
-
1
def set_cache_disabled!
-
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
-
response.headers["Pragma"] = "no-cache"
-
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
-
end
-
-
1
def first_param(key)
-
value = params[key]
-
value ? value.first : nil
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class AssetAuditsController < ApplicationController
-
1
def index
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Assets::CommentsController < ApplicationController
-
1
before_filter :discover_asset
-
-
1
def index
-
@comments = @asset.comments.order("created_at ASC")
-
end
-
-
1
def create
-
@asset.comments.create(:description => params[:comment], :user => current_user)
-
@comments = @asset.comments
-
render :partial => "list", :locals => { :commentable => @asset, :visible => true }
-
end
-
-
1
def destroy
-
comment = Comment.find(params[:id])
-
unless comment.blank?
-
comment.destroy
-
end
-
@comments = @asset.comments
-
render :partial => "list", :locals => { :commentable => @asset, :visible => true }
-
end
-
-
1
private
-
1
def discover_asset
-
@asset = Asset.find(params[:asset_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class AssetsController < ApplicationController
-
-
1
class PlateLayout
-
1
DEFAULT_WELL = { :request => nil, :asset => nil, :error => nil }
-
-
1
attr_reader :width, :height, :wells
-
-
1
def initialize(width, height)
-
@width, @height = width, height
-
@wells = (1..@width*@height).map { |_| DEFAULT_WELL.dup }
-
end
-
-
1
def set_details_for_well_at(location_id, details)
-
assert_valid_location(location_id)
-
@wells[ location_id-1 ] = details
-
end
-
-
1
def size
-
@width * @height
-
end
-
-
1
def cell_name_for_well_at(row, column)
-
Map.find_by_location_id_and_asset_size(((row * self.width) + column +1 ), self.size).description
-
end
-
-
1
def location_for_well_at(row, column)
-
((row * @width) + column) + 1
-
end
-
-
1
def well_at(row, column)
-
location_id = location_for_well_at(row, column)
-
assert_valid_location(location_id)
-
@wells[ location_id-1 ]
-
end
-
-
1
def empty_well_at?(row, column)
-
DEFAULT_WELL == well_at(row, column)
-
end
-
-
1
def good_well_at?(row, column)
-
well = well_at(row, column)
-
[:request, :asset].all? { |field| not well[ field ].nil? }
-
end
-
-
1
def bad_well_at?(row, column)
-
well = well_at(row, column)
-
not well[:error].nil?
-
end
-
-
1
def assert_valid_location(location_id)
-
raise StandardError, "Location out of bounds" unless (1..self.size).include?(location_id)
-
end
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
-
1
class AssetsController < ApplicationController
-
1
include BarcodePrintersController::Print
-
1
before_filter :discover_asset, :only => [:show, :edit, :update, :destory, :summary, :close, :print_assets, :print, :show_plate, :history, :holded_assets]
-
-
1
def index
-
@assets_without_requests = []
-
@assets_with_requests = []
-
if params[:study_id]
-
@study = Study.find(params[:study_id])
-
@assets = @study.assets_through_aliquots.order('name ASC').paginate(:page => params[:page])
-
end
-
-
respond_to do |format|
-
if params[:print]
-
format.html { render :action => :print_index }
-
else
-
format.html
-
end
-
if params[:study_id]
-
format.xml { render :xml => Study.find(params[:study_id]).assets_through_requests.to_xml }
-
elsif params[:sample_id]
-
format.xml { render :xml => Sample.find(params[:sample_id]).assets.to_xml }
-
elsif params[:asset_id]
-
@asset = Asset.find(params[:asset_id])
-
format.xml { render :xml => ["relations" => {"parents" => @asset.parents, "children" => @asset.children}].to_xml }
-
end
-
end
-
end
-
-
1
def show
-
respond_to do |format|
-
format.html
-
format.xml
-
format.json { render :json => @asset }
-
end
-
end
-
-
1
def new
-
@asset = Asset.new
-
@asset_types = { "Library Tube" => 'LibraryTube', "Hybridization Buffer Spiked" => "SpikedBuffer" }
-
@phix_tag = TagGroup.find_by_name(configatron.phix_tag.tag_group_name).tags.select do |t|
-
t.map_id == configatron.phix_tag.tag_map_id
-
end.first
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @asset }
-
end
-
end
-
-
1
def edit
-
@valid_purposes_options = @asset.compatible_purposes.map do |purpose|
-
[purpose.name, purpose.id]
-
end
-
end
-
-
1
def find_parents(text)
-
return [] unless text.present?
-
names = text.lines.map(&:chomp).reject { |l| l.blank? }
-
objects = Asset.find(:all, :conditions => {:id => names})
-
objects += Asset.find(:all, :conditions => {:barcode => names})
-
name_set = Set.new(names)
-
found_set = Set.new(objects.map(&:name))
-
not_found = name_set - found_set
-
raise InvalidInputException, "#{Asset.table_name} #{not_found.to_a.join(", ")} not founds" unless not_found.empty?
-
return objects
-
end
-
-
1
def create
-
-
count = first_param(:count)
-
count = count.present? ? count.to_i : 1
-
saved = true
-
-
begin
-
# Find the parent asset up front
-
parent, parent_param = nil, first_param(:parent_asset)
-
if parent_param.present?
-
parent = Asset.find_from_machine_barcode(parent_param) || Asset.find_by_name(parent_param) || Asset.find_by_id(parent_param)
-
raise StandardError, "Cannot find the parent asset #{parent_param.inspect}" if parent.nil?
-
end
-
-
# Find the tag up front
-
tag, tag_param = nil, first_param(:tag)
-
if tag_param.present?
-
conditions = { :map_id => tag_param }
-
oligo = params[:tag_sequence]
-
conditions[:oligo] = oligo.first.upcase if oligo.present? and oligo.first.present?
-
tag = Tag.first(:conditions => conditions) or raise StandardError, "Cannot find tag #{tag_param.inspect}"
-
end
-
-
sti_type = params[:asset].delete(:sti_type) or raise StandardError, "No asset type specified"
-
asset_class = sti_type.constantize
-
-
ActiveRecord::Base.transaction do
-
@assets = (1..count).map do |n|
-
asset = asset_class.new(params[:asset]) do |asset|
-
asset.name += " ##{n}" if count !=1
-
end
-
# from asset
-
if parent.present?
-
parent_volume, parent_used = params[:parent_volume], parent
-
if parent_volume.present? and parent_volume.first.present?
-
-
extract = parent_used.transfer(parent_volume.first)
-
-
if asset.volume
-
parent_used = extract
-
asset.save!
-
elsif asset.is_a?(SpikedBuffer) and !parent_used.is_a?(SpikedBuffer)
-
raise StandardError, "Enter a volume"
-
else
-
# Discard the 'asset' that was build initially as it is being replaced by the asset
-
# created from the extraction process.
-
extract.update_attributes!(:name => asset.name)
-
asset, parent_used = extract, nil
-
end
-
end
-
# We must copy the aliquots of the 'extract' to the asset, otherwise the asset remains empty.
-
asset.aliquots = parent_used.aliquots.map(&:dup) unless parent_used.nil?
-
asset.add_parent(parent_used)
-
else
-
# All new assets are assumed to have a phiX sample in them as that's the only asset that
-
# is created this way.
-
asset.save!
-
aliquot_attributes = { :sample => SpikedBuffer.phiX_sample, :study_id => 198 }
-
aliquot_attributes[:library] = asset if asset.is_a?(LibraryTube) or asset.is_a?(SpikedBuffer)
-
asset.aliquots.create!(aliquot_attributes)
-
end
-
tag.tag!(asset) if tag.present?
-
asset.update_attributes!(:barcode => AssetBarcode.new_barcode) if asset.barcode.nil?
-
asset.comments.create!(:user => current_user, :description => "asset has been created by #{current_user.login}")
-
asset
-
end
-
end # transaction
-
rescue Asset::VolumeError => ex
-
saved = false
-
flash[:error] = ex.message
-
rescue => exception
-
saved = false
-
flash[:error] = exception.message
-
end
-
-
respond_to do |format|
-
if saved
-
flash[:notice] = 'Asset was successfully created.'
-
format.html { render :action => :create}
-
format.xml { render :xml => @assets, :status => :created, :location => assets_url(@assets) }
-
format.json { render :json => @assets, :status => :created, :location => assets_url(@assets) }
-
else
-
format.html { redirect_to :action => "new" }
-
format.xml { render :xml => @assets.errors, :status => :unprocessable_entity }
-
format.json { render :json => @assets.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def history
-
respond_to do |format|
-
format.html
-
format.xml { @request.events.to_xml }
-
format.json { @request.events.to_json }
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if (@asset.update_attributes(params[:asset]) && @asset.update_attributes(params[:lane]))
-
flash[:notice] = 'Asset was successfully updated.'
-
unless params[:lab_view]
-
format.html { redirect_to(:action => :show, :id => @asset.id) }
-
format.xml { head :ok }
-
else
-
format.html { redirect_to(:action => :lab_view, :barcode => @asset.barcode) }
-
end
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @asset.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@asset.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(assets_url) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def summary
-
@summary = UiHelper::Summary.new({:per_page => 25, :page => params[:page]})
-
@summary.load_item(@asset)
-
end
-
-
1
def close
-
@asset.closed = !@asset.closed
-
@asset.save
-
respond_to do |format|
-
if @asset.closed
-
flash[:notice] = "Asset #{@asset.name} was closed."
-
else
-
flash[:notice] = "Asset #{@asset.name} was opened."
-
end
-
format.html { redirect_to(asset_url(@asset)) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def print_labels
-
print_asset_labels(new_asset_url, new_asset_url)
-
end
-
-
1
def print_assets
-
params[:printables]={@asset =>1}
-
return print_asset_labels(asset_url(@asset), asset_url(@asset))
-
end
-
-
1
def show_plate
-
end
-
-
1
before_filter :prepare_asset, :only => [ :new_request, :create_request ]
-
-
1
def prepare_asset
-
@asset = Asset.find(params[:id])
-
end
-
1
private :prepare_asset
-
-
1
def new_request_for_current_asset
-
new_request_asset_path(@asset, {:study_id => @study.try(:id), :project_id => @project.try(:id), :request_type_id => @request_type.id})
-
end
-
1
private :new_request_for_current_asset
-
-
1
def new_request
-
@request_types = RequestType.applicable_for_asset(@asset)
-
@study = @asset.studies.first
-
@project = @asset.projects.first || @asset.studies.first && @asset.studies.first.projects.first
-
end
-
-
1
def create_request
-
@request_type = RequestType.find(params[:request_type_id])
-
@study = Study.find(params[:study_id]) unless params[:cross_study_request].present?
-
@project = Project.find(params[:project_id]) unless params[:cross_project_request].present?
-
-
request_options = params.fetch(:request, {}).fetch(:request_metadata_attributes, {})
-
request_options[:multiplier] = { @request_type.id => params[:count].to_i } unless params[:count].blank?
-
submission = ReRequestSubmission.build!(
-
:study => @study,
-
:project => @project,
-
:workflow => @request_type.workflow,
-
:user => current_user,
-
:assets => [ @asset ],
-
:request_types => [ @request_type.id ],
-
:request_options => request_options,
-
:comments => params[:comments],
-
:priority => params[:priority]
-
)
-
-
respond_to do |format|
-
flash[:notice] = 'Created request'
-
-
format.html { redirect_to new_request_for_current_asset }
-
format.json { render :json => submission.requests, :status => :created }
-
end
-
rescue Submission::ProjectValidation::Error => exception
-
respond_to do |format|
-
flash[:error] = exception.message
-
format.html { redirect_to new_request_for_current_asset }
-
format.json { render :json => exception.message, :status => :unprocessable_entity }
-
end
-
rescue ActiveRecord::RecordNotFound => exception
-
respond_to do |format|
-
flash[:error] = exception.message
-
format.html { redirect_to new_request_for_current_asset }
-
format.json { render :json => exception.message, :status => :precondition_failed }
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
respond_to do |format|
-
flash[:error] = exception.message
-
format.html { redirect_to new_request_for_current_asset }
-
format.json { render :json => exception.message, :status => :precondition_failed }
-
end
-
end
-
-
1
def get_barcode
-
barcode = Asset.get_barcode_from_params(params)
-
render(:text => "#{Barcode.barcode_to_human(barcode)} => #{barcode}")
-
end
-
-
1
def get_barcode_from_params(params)
-
prefix, asset = 'NT', nil
-
if params[:prefix]
-
prefix = params[:prefix]
-
else
-
begin
-
asset = Asset.find(params[:id])
-
rescue
-
end
-
end
-
-
if asset and asset.barcode
-
Barcode.calculate_barcode(asset.prefix, asset.barcode.to_i)
-
else
-
Barcode.calculate_barcode(prefix, params[:id].to_i)
-
end
-
end
-
1
private :get_barcode_from_params
-
-
1
def lookup
-
if params[:asset] && params[:asset][:barcode]
-
id = params[:asset][:barcode][3,7]
-
@assets = Asset.find(:all, :conditions => {:barcode => id}).paginate :per_page => 50, :page => params[:page]
-
-
if @assets.size == 1
-
redirect_to @assets.first
-
elsif @assets.size == 0
-
flash.now[:error] = "No asset found with barcode #{params[:asset][:barcode]}"
-
respond_to do |format|
-
format.html { render :action => "lookup" }
-
format.xml { render :xml => @assets.to_xml }
-
end
-
else
-
respond_to do |format|
-
format.html { render :action => "index" }
-
format.xml { render :xml => @assets.to_xml }
-
end
-
end
-
end
-
end
-
-
1
def reset_values_for_move
-
render :layout => false
-
end
-
-
1
def find_by_barcode
-
end
-
-
1
def lab_view
-
barcode = params[:barcode]
-
if barcode.blank?
-
redirect_to :action => "find_by_barcode"
-
else
-
if barcode.size == 13 && Barcode.check_EAN(barcode)
-
num_prefix, number, _ = Barcode.split_barcode(barcode)
-
prefix = BarcodePrefix.find_by_prefix(Barcode.prefix_to_human(num_prefix))
-
@asset = Asset.find_by_barcode_and_barcode_prefix_id(number,prefix.id)
-
else
-
@asset = Asset.find_by_barcode(barcode)
-
end
-
-
if @asset.nil?
-
flash[:error] = "Unable to find anything with this barcode"
-
redirect_to :action => "find_by_barcode"
-
end
-
end
-
end
-
-
1
def create_stocks
-
params[:assets].each do |id, params|
-
asset = Asset.find(id)
-
stock_asset = asset.create_stock_asset!(
-
:name => params[:name],
-
:volume => params[:volume],
-
:concentration => params[:concentration]
-
)
-
stock_asset.assign_relationships(asset.parents, asset)
-
end
-
-
batch = Batch.find(params[:batch_id])
-
redirect_to batch_path(batch)
-
end
-
-
1
private
-
1
def discover_asset
-
@asset = Asset.find(params[:id], :include => { :requests => :request_metadata })
-
end
-
-
1
def check_valid_values(params = nil)
-
if (params[:study_id_to] == "0") || (params[:study_id_from] == "0")
-
flash[:error] = "You have to select 'Study From' and 'Study To'"
-
return false
-
else
-
study_from = Study.find(params[:study_id_from])
-
study_to = Study.find(params[:study_id_to])
-
if study_to.name.eql?(study_from.name)
-
flash[:error] = "You can't select the same Study."
-
return false
-
elsif params[:asset_group_id] == "0" && params[:new_assets_name].empty?
-
flash[:error] = "You must indicate an 'Asset Group'."
-
return false
-
elsif !(params[:asset_group_id] == "0") && !(params[:new_assets_name].empty?)
-
flash[:error] = "You can select only an Asset Group!"
-
return false
-
elsif AssetGroup.find_by_name(params[:new_assets_name])
-
flash[:error] = "The name of Asset Group exists!"
-
return false
-
end
-
end
-
return true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
class BarcodePrintersController < ApplicationController
-
-
1
before_filter :admin_login_required
-
-
1
def index
-
@barcode_printers = BarcodePrinter.find(:all)
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def show
-
@barcode_printer = BarcodePrinter.find(params[:id])
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def new
-
@barcode_printer = BarcodePrinter.new
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def edit
-
@barcode_printer = BarcodePrinter.find(params[:id])
-
end
-
-
1
def create
-
@barcode_printer = BarcodePrinter.new(params[:barcode_printer])
-
@barcode_printer.barcode_printer_type = BarcodePrinterType.find(params[:barcode_printer_type_id])
-
-
respond_to do |format|
-
if @barcode_printer.save
-
flash[:notice] = 'Barcode Printer was successfully created.'
-
format.html { redirect_to(barcode_printers_url) }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
1
def update
-
@barcode_printer = BarcodePrinter.find(params[:id])
-
@barcode_printer.barcode_printer_type = BarcodePrinterType.find(params[:barcode_printer_type_id])
-
-
respond_to do |format|
-
if @barcode_printer.update_attributes(params[:barcode_printer])
-
flash[:notice] = 'Barcode Printer was successfully updated.'
-
format.html { redirect_to(barcode_printers_url) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
1
def destroy
-
@barcode_printer = BarcodePrinter.find(params[:id])
-
@barcode_printer.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(barcode_printers_url) }
-
end
-
end
-
# This module define common behavior used by other controller to print things
-
1
module Print
-
1
def print_asset_labels(succes_url, failure_url)
-
assets = params[:printables]
-
prefix = nil
-
unless assets.nil?
-
printables = []
-
assets = assets.keys
-
assets.sort{ |a,b| b.to_i <=> a.to_i }.each do |id|
-
asset = Asset.find(id)
-
prefix = asset.prefix
-
unless asset.barcode.present?
-
asset.barcode = AssetBarcode.new_barcode
-
asset.save!
-
end
-
printables.push PrintBarcode::Label.new({ :number => asset.barcode, :study => asset.name_for_label.to_s, :prefix => prefix, :suffix => "" })
-
end
-
-
unless printables.empty?
-
BarcodePrinter.print(printables, params[:printer], prefix)
-
end
-
end
-
flash[:notice] = "Your labels have been sent to printer #{params[:printer]}."
-
redirect_to succes_url
-
-
rescue Savon::Error, Sanger::Barcode::Printing::BarcodeException => e
-
if e.kind_of? Savon::Error
-
flash[:warning] = "There is a problem with the selected printer. Please report it to Systems."
-
else
-
flash[:error] = "There was a problem with the printer. Select another and try again."
-
end
-
Rails.logger.error($!)
-
-
redirect_to failure_url
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Batches::CommentsController < ApplicationController
-
1
before_filter :discover_batch
-
-
1
def index
-
@comments = @batch.comments.all(:order => "created_at ASC")
-
end
-
-
1
def create
-
@batch.comments.create(:description => params[:comment], :user_id => current_user.id)
-
@comments = @batch.comments
-
render :partial => "list", :locals => { :commentable => @batch, :visible => true }
-
end
-
-
1
def destroy
-
comment = Comment.find(params[:id])
-
unless comment.blank?
-
comment.destroy
-
end
-
@comments = @batch.comments
-
render :partial => "list", :locals => { :commentable => @batch, :visible => true }
-
end
-
-
1
private
-
1
def discover_batch
-
@batch = Batch.find(params[:batch_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Batches::RequestsController < ApplicationController
-
-
1
def index
-
requests = Batch.find(params[:batch_id]).requests
-
respond_to do |format|
-
format.xml { render :xml => requests.to_xml }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class BatchesController < ApplicationController
-
1
include XmlCacheHelper::ControllerHelper
-
-
1
before_filter :login_required, :except => [:released, :evaluations_counter, :qc_criteria]
-
1
before_filter :find_batch_by_id, :only => [:show,:edit, :update, :destroy, :qc_information, :qc_batch, :save, :fail, :fail_items, :fail_batch, :assign_batch, :control, :add_control, :print_labels, :print_plate_labels, :print_multiplex_labels, :print, :verify, :verify_tube_layout, :reset_batch, :previous_qc_state, :filtered, :swap, :download_spreadsheet, :gwl_file, :pulldown_batch_report, :pacbio_sample_sheet, :sample_prep_worksheet]
-
1
before_filter :find_batch_by_batch_id, :only => [:sort, :print_multiplex_barcodes, :print_pulldown_multiplex_tube_labels, :print_plate_barcodes, :print_barcodes]
-
-
1
def index
-
if logged_in?
-
@user = current_user
-
assigned_batches = Batch.find_all_by_assignee_id(@user.id)
-
@batches = (@user.batches + assigned_batches).sort_by {|batch| batch.id}.reverse
-
else
-
# not reachable !!! if not login redirect to login
-
@batches = Batch.find(:all)
-
end
-
if params[:request_id]
-
@batches = [Request.find(params[:request_id]).batch].compact
-
end
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @batches.to_xml }
-
format.json { render :json => @batches.to_json.gsub(/null/, "\"\"") }
-
end
-
end
-
-
1
def show
-
@submenu_presenter = Presenters::BatchSubmenuPresenter.new(current_user, @batch)
-
-
@pipeline = @batch.pipeline
-
@tasks = @batch.tasks.sort_by(&:sorted)
-
@rits = @pipeline.request_information_types
-
@input_assets, @output_assets = []
-
# Should it be this?
-
# @input_assets, @output_assets = [], []
-
-
if @pipeline.group_by_parent
-
@input_assets = @batch.input_group
-
@output_assets = @batch.output_group_by_holder unless @pipeline.is_a?(PulldownMultiplexLibraryPreparationPipeline)
-
end
-
-
respond_to do |format|
-
format.html
-
format.xml { cache_xml_response(@batch) }
-
end
-
end
-
-
1
def edit
-
@rits = @batch.pipeline.request_information_types
-
@requests = @batch.ordered_requests
-
@users = User.all
-
@controls = @batch.pipeline.controls
-
end
-
-
1
def update
-
respond_to do |format|
-
if @batch.update_attributes(params[:batch])
-
flash[:notice] = 'Batch was successfully updated.'
-
format.html { redirect_to batch_url(@batch) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @batch.errors.to_xml }
-
end
-
end
-
end
-
-
1
def hide_from_inbox(requests)
-
ActiveRecord::Base.transaction do
-
requests.map(&:hold!)
-
end
-
-
respond_to do |format|
-
flash[:notice] = 'Requests hidden from inbox'
-
format.html { redirect_to :controller => :pipelines, :action => :show, :id => @pipeline.id }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def cancel_requests(requests)
-
ActiveRecord::Base.transaction do
-
requests.map(&:cancel_before_started!)
-
end
-
-
respond_to do |format|
-
flash[:notice] = 'Requests canceled'
-
format.html { redirect_to :controller => :pipelines, :action => :show, :id => @pipeline.id }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def create
-
ActiveRecord::Base.transaction do
-
@pipeline = Pipeline.find(params[:id])
-
-
unless @pipeline.valid_number_of_checked_request_groups?(params)
-
return pipeline_error_on_batch_creation("Too many request groups selected, maximum is #{@pipeline.max_number_of_groups}")
-
end
-
-
requests = @pipeline.extract_requests_from_input_params(params)
-
-
return pipeline_error_on_batch_creation("Maximum batch size is #{@pipeline.max_size}") if @pipeline.max_size && requests.size > @pipeline.max_size
-
return pipeline_error_on_batch_creation("All plates in a submission must be selected") unless @pipeline.all_requests_from_submissions_selected?(requests)
-
-
return hide_from_inbox(requests) if params[:action_on_requests] == "hide_from_inbox"
-
return cancel_requests(requests) if params[:action_on_requests] == "cancel_requests"
-
-
@batch = @pipeline.batches.create!(:requests => requests, :user => current_user)
-
-
end # of transaction
-
-
respond_to do |format|
-
format.html {
-
if @pipeline.has_controls?
-
flash[:notice] = 'Batch created - now add a control'
-
redirect_to :action => :control, :id => @batch.id
-
else
-
redirect_to :action => :show, :id => @batch.id
-
end
-
}
-
format.xml { head :created, :location => batch_url(@batch) }
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
respond_to do |format|
-
format.html {
-
flash[:error] = exception.record.errors.full_messages
-
redirect_to(pipeline_path(@pipeline))
-
}
-
format.xml { render :xml => @batch.errors.to_xml }
-
end
-
end
-
-
1
def pipeline
-
@batches = Batch.all(:conditions => {:pipeline_id => params[:id]}, :order => "id DESC", :include => [:requests, :user, :pipeline])
-
end
-
-
# Used by Quality Control Pipeline view or remote sources to add a Batch ID to QC queue
-
1
def start_automatic_qc
-
if request.post?
-
@batch = Batch.find(params[:id])
-
-
submitted = @batch.submit_to_qc_queue
-
-
if submitted
-
@batch.lab_events.create(:description => "Submitted to QC", :message => "Batch #{@batch.id} was submitted to QC queue", :user_id => @current_user.id)
-
respond_to do |format|
-
message = "Batch #{@batch.id} was submitted to QC queue"
-
format.html do
-
flash[:info] = message
-
redirect_to request.env["HTTP_REFERER"] || 'javascript:history.back()'
-
end
-
format.xml { render :text => nil, :status => :success }
-
end
-
else
-
respond_to do |format|
-
message = "Batch #{@batch.id} was not submitted to QC queue!"
-
format.html do
-
flash[:warning] = message
-
redirect_to request.env["HTTP_REFERER"] || 'javascript:history.back()'
-
end
-
format.xml do
-
render :xml => {:error => message}.to_xml(:root => :errors), :status => :bad_request
-
end
-
end
-
end
-
else
-
respond_to do |format|
-
message = "There was a problem with the request. HTTP POST method was not used."
-
format.html do
-
flash[:error] = message
-
redirect_to request.env["HTTP_REFERER"] || 'javascript:history.back()'
-
end
-
format.xml do
-
errors = {:error => message}
-
render :xml => errors.to_xml(:root => :errors), :status => :method_not_allowed
-
end
-
end
-
end
-
end
-
-
1
def qc_information
-
respond_to do |format|
-
format.html
-
format.json do
-
b = @batch.formatted_batch_qc_details
-
render :json => b.to_json.gsub(/null/, "\"\"")
-
end
-
end
-
end
-
-
# Deals with QC failures leaving batches and items statuses intact
-
1
def qc_batch
-
@batch.qc_complete
-
-
@batch.batch_requests.each do |br|
-
if br && params["#{br.request_id}"]
-
qc_state = params["#{br.request_id}"]["qc_state"]
-
target = br.request.target_asset
-
if qc_state == "fail"
-
target.set_qc_state("failed")
-
EventSender.send_fail_event(br.request_id, "", "Failed manual QC", @batch.id)
-
elsif qc_state == "pass"
-
target.set_qc_state("passed")
-
EventSender.send_pass_event(br.request_id, "", "Passed manual QC", @batch.id)
-
end
-
target.save
-
end
-
end
-
-
@batch.release_without_user!
-
-
redirect_to :controller => :pipelines, :action => :show, :id => @batch.qc_pipeline_id
-
end
-
-
1
def pending
-
@pipeline = Pipeline.find(params[:id])
-
@batches = @pipeline.batches.pending.all(:order => "id DESC", :include => [:requests, :user, :pipeline])
-
end
-
-
1
def started
-
@pipeline = Pipeline.find(params[:id])
-
@batches = @pipeline.batches.started.all(:order => "id DESC", :include => [:requests, :user, :pipeline])
-
end
-
-
1
def released
-
@pipeline = Pipeline.find(params[:id])
-
-
@batches = @pipeline.batches.released.all(:order => "id DESC", :include => [:user ])
-
respond_to do |format|
-
format.html
-
format.xml { render :layout => false }
-
end
-
end
-
-
1
def completed
-
@pipeline = Pipeline.find(params[:id])
-
@batches = @pipeline.batches.completed.all(:order => "id DESC", :include => [:requests, :user, :pipeline])
-
end
-
-
1
def failed
-
@pipeline = Pipeline.find(params[:id])
-
@batches = @pipeline.batches.failed.all(:order => "id DESC", :include => [:requests, :user, :pipeline])
-
end
-
-
1
def fail
-
if @batch.workflow.source_is_internal?
-
@fail_reasons = FAILURE_REASONS["internal"]
-
else
-
@fail_reasons = FAILURE_REASONS["external"]
-
end
-
end
-
-
1
def quality_control
-
@qc_pipeline = Pipeline.find(params[:id])
-
conditions_query = []
-
if params["state"]
-
conditions_query = ["state = ? AND qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", params["state"], params["qc_state"], @qc_pipeline.id, @qc_pipeline.cluster_formation_pipeline_id]
-
else
-
conditions_query = ["qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", params["qc_state"], @qc_pipeline.id, @qc_pipeline.cluster_formation_pipeline_id]
-
end
-
-
@batches = Batch.find(:all, :conditions => conditions_query, :include => [:user], :order => "created_at ASC")
-
end
-
-
1
def fail_items
-
ActiveRecord::Base.transaction do
-
unless params[:failure][:reason].empty?
-
reason = params[:failure][:reason]
-
comment = params[:failure][:comment]
-
requests = params[:requested_fail] || {}
-
fail_but_charge = params[:failure][:fail_but_charge]=='1'
-
requests_for_removal = params[:requested_remove] || {}
-
# Check to see if the user is trying to remove AND fail the same request.
-
diff = requests_for_removal.keys & requests.keys
-
-
unless diff.empty?
-
flash[:error] = "Fail and remove were both selected for the following - #{diff.to_sentence} this is not supported."
-
else
-
if requests.empty? && requests_for_removal.empty?
-
flash[:error] = "Please select an item to fail or remove"
-
else
-
unless requests.empty?
-
@batch.fail_batch_items(requests, reason, comment, fail_but_charge)
-
flash[:notice] = "#{requests.keys.to_sentence} set to failed.#{fail_but_charge ? ' The customer will still be charged.':''}"
-
end
-
-
unless requests_for_removal.empty?
-
@batch.remove_request_ids(requests_for_removal.keys, reason, comment)
-
flash[:notice] = "#{requests_for_removal.keys.to_sentence} removed."
-
end
-
end
-
end
-
redirect_to :action => "fail", :id => @batch.id
-
else
-
flash[:error] = "Please specify a failure reason for this batch"
-
redirect_to :action => :fail, :id => @batch.id
-
end
-
end
-
end
-
-
1
def sort
-
@batch.assign_positions_to_requests!(params['requests_list'].map(&:to_i))
-
render :nothing => true
-
end
-
-
1
def save
-
redirect_to :action => :show, :id => @batch.id
-
end
-
-
1
def assign_batch
-
@user = User.find(params[:assignee][:id])
-
@batch.assignee_id = @user.id
-
if @batch.save
-
flash[:notice] = "Batch assigned to #{@user.login}"
-
redirect_to :action => "edit", :id => @batch.id
-
else
-
flash[:notice] = "Could not assign batch"
-
redirect_to :action => "edit", :id => @batch.id
-
end
-
end
-
-
1
def control
-
@rits = @batch.pipeline.request_information_types
-
@controls = @batch.pipeline.controls
-
end
-
-
1
def add_control
-
@control = Control.find(params[:control][:id])
-
control_count = params[:control][:count].to_i
-
-
if control_count > @batch.space_left
-
flash[:error] = "Can't assign more than #{@batch.space_left} control to this batch"
-
redirect_to :action => "control", :id => @batch.id
-
return
-
elsif control_count < 0
-
flash[:error] = "This batch needs at least one control"
-
redirect_to :action => "control", :id => @batch.id
-
return
-
end
-
control_count = @batch.add_control(@control.name, control_count)
-
-
redirect_to batch_path(@batch)
-
end
-
-
1
def create_training_batch
-
control = Control.find(params[:control][:id])
-
pipeline = control.pipeline
-
limit = pipeline.item_limit
-
-
batch = pipeline.batches.create!(:item_limit => limit, :user_id => current_user.id)
-
batch.add_control(control.name, pipeline.item_limit)
-
-
flash[:notice] = 'Training batch created'
-
redirect_to :action => "show", :id => batch.id
-
end
-
-
1
def evaluations_counter
-
@ev = BatchStatus.find(params[:id])
-
render :partial => 'evaluations_counter'
-
end
-
-
1
def print_labels
-
end
-
-
1
def print_stock_labels
-
@batch = Batch.find(params[:id])
-
end
-
-
1
def print_plate_labels
-
@pipeline = @batch.pipeline
-
@output_barcodes = []
-
-
@output_assets = @batch.plate_group_barcodes || []
-
-
@output_assets.each do |parent, children|
-
unless parent.nil?
-
plate_barcode = parent.barcode
-
unless plate_barcode.blank?
-
@output_barcodes << plate_barcode
-
end
-
end
-
end
-
-
if @output_barcodes.blank?
-
flash[:error] = "Output plates do not have barcodes to print"
-
redirect_to :controller => 'batches', :action => 'show', :id => @batch.id
-
else
-
end
-
end
-
-
1
def print_multiplex_labels
-
request = @batch.requests.first
-
unless request.tag_number.nil?
-
if ! request.target_asset.nil? && ! request.target_asset.children.empty?
-
# We are trying to find the MX library tube or the stock MX library
-
# tube. I've added a filter so it doesn't pick up Lanes.
-
children = request.target_asset.children.last.children.select { |a| a.is_a?(Tube) }
-
if children.empty?
-
@asset = request.target_asset.children.last
-
else
-
@asset = request.target_asset.children.last.children.last
-
end
-
else
-
flash[:notice] = "There is no multiplexed library available."
-
end
-
else
-
flash[:error] = "No tags have been assigned."
-
end
-
# @assets = @batch.multiplexed_items_with_unique_library_ids
-
end
-
-
1
def print_stock_multiplex_labels
-
@batch = Batch.find(params[:id])
-
request = @batch.requests.first
-
pooled_library = request.target_asset.children.first
-
stock_multiplexed_tube = nil
-
-
if pooled_library.is_a_stock_asset?
-
stock_multiplexed_tube = pooled_library
-
elsif pooled_library.has_stock_asset?
-
stock_multiplexed_tube = pooled_library.stock_asset
-
end
-
-
if stock_multiplexed_tube.nil?
-
flash[:notice] = "There is no stock multiplexed library available."
-
redirect_to :controller => 'batches', :action => 'show', :id => @batch.id
-
else
-
@asset = stock_multiplexed_tube
-
end
-
end
-
-
1
def print_multiplex_barcodes
-
printables = []
-
count = params[:count].to_i
-
-
params[:printable].each do |key, value|
-
if value == 'on'
-
asset = Asset.find(key)
-
if @batch.multiplexed?
-
count.times do
-
printables.push PrintBarcode::Label.new({ :number => asset.barcode, :study => "(p) #{asset.name}" })
-
end
-
end
-
end
-
end
-
-
unless printables.empty?
-
asset = @batch.assets.first
-
begin
-
printables.sort! {|a,b| a.number <=> b.number }
-
BarcodePrinter.print(printables, params[:printer], asset.prefix, "short")
-
rescue PrintBarcode::BarcodeException
-
flash[:error] = "Label printing to #{params[:printer]} failed: #{$!}."
-
rescue SOAP::FaultError
-
flash[:warning] = "There is a problem with the selected printer. Please report it to Systems."
-
else
-
flash[:notice] = "Your labels have been printed to #{params[:printer]}."
-
end
-
end
-
redirect_to :controller => 'batches', :action => 'show', :id => @batch.id
-
end
-
-
1
def print_plate_barcodes
-
printables = []
-
count = params[:count].to_i
-
params[:printable].each do |key, value|
-
if value == 'on'
-
label = key
-
identifier = key
-
count.times do
-
printables.push PrintBarcode::Label.new({ :number => identifier, :study => label, :batch => @batch })
-
end
-
end
-
end
-
unless printables.empty?
-
begin
-
printables.sort! {|a,b| a.number <=> b.number }
-
BarcodePrinter.print(printables, params[:printer], "DN", "cherrypick",@batch.study.abbreviation, current_user.login)
-
rescue PrintBarcode::BarcodeException
-
flash[:error] = "Label printing to #{params[:printer]} failed: #{$!}."
-
rescue SOAP::FaultError
-
flash[:warning] = "There is a problem with the selected printer. Please report it to Systems."
-
else
-
flash[:notice] = "Your labels have been printed to #{params[:printer]}."
-
end
-
end
-
redirect_to :controller => 'batches', :action => 'show', :id => @batch.id
-
end
-
-
-
1
def print_barcodes
-
unless @batch.requests.empty?
-
asset = @batch.requests.first.target_asset
-
printables = []
-
count = params[:count].to_i
-
params[:printable].each do |key, value|
-
if value == 'on'
-
request = Request.find(key)
-
if params[:stock]
-
if @batch.multiplexed?
-
stock = request.target_asset.children.first
-
identifier = stock.barcode
-
label = stock.name
-
else
-
stock = request.target_asset.stock_asset
-
identifier = stock.barcode
-
label = stock.name
-
end
-
else
-
if @batch.multiplexed?
-
unless request.tag_number.nil?
-
label = "(#{request.tag_number}) #{request.target_asset.id}"
-
identifier = request.target_asset.barcode
-
else
-
label = request.target_asset.name
-
identifier = request.target_asset.barcode
-
end
-
else
-
label = request.target_asset.tube_name
-
identifier = request.target_asset.barcode
-
end
-
end
-
count.times do
-
printables.push PrintBarcode::Label.new({ :number => identifier, :study => label })
-
end
-
end
-
end
-
unless printables.empty?
-
begin
-
printables.sort! {|a,b| b.number <=> a.number }
-
BarcodePrinter.print(printables, params[:printer], asset.prefix, "short")
-
rescue PrintBarcode::BarcodeException
-
flash[:error] = "Label printing to #{params[:printer]} failed: #{$!}."
-
rescue SOAP::FaultError
-
flash[:warning] = "There is a problem with the selected printer. Please report it to Systems."
-
else
-
flash[:notice] = "Your labels have been printed to #{params[:printer]}."
-
end
-
end
-
else
-
flash[:notice] = "Your batch contains no requests."
-
end
-
redirect_to :controller => 'batches', :action => 'show', :id => @batch.id
-
end
-
-
1
def print
-
if params[:task_id]
-
@task = Task.find(params[:task_id])
-
end
-
-
@workflow = @batch.workflow
-
@pipeline = @batch.pipeline
-
@comments = @batch.comments
-
-
# TODO: Re-factor this.
-
-
if @pipeline.is_a?(PulldownMultiplexLibraryPreparationPipeline)
-
@plate = @batch.requests.first.asset.plate
-
render :action => "pulldown_worksheet", :layout => false
-
elsif @pipeline.is_a?(CherrypickingPipeline)
-
if params[:barcode]
-
@plates = [Plate.find_by_barcode(params[:barcode])]
-
else
-
@plates = @batch.output_plates
-
end
-
render :action => "cherrypick_worksheet", :layout => false
-
elsif @batch.has_item_limit?
-
# Currently cluster formation pipelines
-
render :action => "simplified_worksheet", :layout => false
-
elsif @batch.multiplexed?
-
if @task
-
render :action => "multiplexed_library_worksheet", :layout => false, :locals => {:task => @task}
-
else
-
render :action => "multiplexed_library_worksheet", :layout => false
-
end
-
else
-
if @task
-
render :action => "detailed_worksheet", :layout => false, :locals => {:task => @task}
-
else
-
render :action => "detailed_worksheet", :layout => false
-
end
-
end
-
end
-
-
1
def verify
-
@requests = @batch.ordered_requests
-
@pipeline = @batch.pipeline
-
@count = 8
-
end
-
-
1
def verify_tube_layout
-
tube_barcodes = {}
-
unless params.empty?
-
8.times do |i|
-
if params["barcode_#{i}"]
-
tube_barcodes["#{i + 1}"] = Barcode.split_barcode("#{params["barcode_#{i}"]}")[1]
-
end
-
end
-
end
-
results = @batch.verify_tube_layout(tube_barcodes, current_user)
-
-
if results
-
flash[:notice] = "All of the tubes are in their correct positions."
-
redirect_to batch_path(@batch)
-
elsif ! results
-
flash[:error] = @batch.errors.full_messages.sort
-
redirect_to :action => :verify, :id => @batch.id
-
end
-
end
-
-
1
def reset_batch
-
pipeline = @batch.pipeline
-
@batch.reset!(current_user)
-
flash[:notice] = "Batch #{@batch.id} has been reset"
-
redirect_to :controller => "pipelines", :action => :show, :id => pipeline.id
-
end
-
-
1
def previous_qc_state
-
@batch.qc_previous_state!(current_user)
-
@batch.save
-
flash[:notice] = "Batch #{@batch.id} reset to state #{@batch.qc_state}"
-
redirect_to batch_url(@batch)
-
end
-
-
1
def filtered
-
end
-
-
1
def swap
-
if @batch.swap(current_user, {"batch_1" => {"id"=>params["batch"]["1"], "lane"=>params["batch"]["position"]["1"]},
-
"batch_2" => {"id"=>params["batch"]["2"], "lane"=>params["batch"]["position"]["2"]}
-
})
-
flash[:notice] = "Successfully swapped lane positions"
-
redirect_to batch_path(@batch)
-
else
-
flash[:error] = @batch.errors.full_messages.join("<br />")
-
redirect_to :action => :filtered, :id => @batch.id
-
end
-
end
-
-
1
def download_spreadsheet
-
csv_string = Tasks::PlateTemplateHandler.generate_spreadsheet(@batch)
-
send_data csv_string, :type => "text/plain",
-
:filename=>"#{@batch.id}_cherrypick_layout.csv",
-
:disposition => 'attachment'
-
end
-
-
1
def gwl_file
-
@plate_barcode = @batch.plate_barcode(params[:barcode])
-
tecan_gwl_file_as_string = @batch.tecan_gwl_file_as_text(@plate_barcode,
-
@batch.total_volume_to_cherrypick,
-
params[:plate_type])
-
send_data tecan_gwl_file_as_string, :type => "text/plain",
-
:filename=>"#{@batch.id}_batch_#{@plate_barcode}.gwl",
-
:disposition => 'attachment'
-
end
-
-
1
def find_batch_by_id
-
@batch = Batch.find(params[:id])
-
end
-
-
1
def find_batch_by_batch_id
-
@batch = Batch.find(params[:batch_id])
-
end
-
-
1
def new_stock_assets
-
@batch = Batch.find(params[:id])
-
unless @batch.requests.empty?
-
@batch_assets = []
-
unless @batch.multiplexed?
-
@batch_assets = @batch.requests.map(&:target_asset)
-
@batch_assets.delete_if{ |a| a.has_stock_asset? }
-
if @batch_assets.empty?
-
flash[:error] = "Stock tubes already exist for everything."
-
redirect_to batch_path(@batch)
-
end
-
else
-
unless @batch.requests.first.target_asset.children.empty?
-
multiplexed_library = @batch.requests.first.target_asset.children.first
-
-
if ! multiplexed_library.has_stock_asset? && ! multiplexed_library.is_a_stock_asset?
-
@batch_assets = [multiplexed_library]
-
else
-
flash[:error] = "Already has a Stock tube."
-
redirect_to batch_path(@batch)
-
end
-
else
-
flash[:error] = "There's no multiplexed library tube available to have a stock tube."
-
redirect_to batch_path(@batch)
-
end
-
end
-
@assets = {}
-
@batch_assets.each do |batch_asset|
-
@assets[batch_asset.id] = batch_asset.new_stock_asset
-
end
-
end
-
end
-
-
1
def edit_volume_and_concentration
-
@batch = Batch.find(params[:id])
-
end
-
-
1
def update_volume_and_concentration
-
@batch = Batch.find(params[:batch_id])
-
-
params[:assets].each do |id, values|
-
asset = Asset.find(id)
-
asset.volume = values[:volume]
-
asset.concentration = values[:concentration]
-
asset.save
-
end
-
-
redirect_to batch_path(@batch)
-
end
-
-
1
def pulldown_batch_report
-
csv_string = @batch.pulldown_batch_report
-
send_data csv_string, :type => "text/plain",
-
:filename=>"batch_#{@batch.id}_report.csv",
-
:disposition => 'attachment'
-
-
end
-
-
1
def pacbio_sample_sheet
-
csv_string = PacBio::SampleSheet.new.create_csv_from_batch(@batch)
-
send_data csv_string, :type => "text/plain",
-
:filename=>"batch_#{@batch.id}_sample_sheet.csv",
-
:disposition => 'attachment'
-
end
-
-
1
def sample_prep_worksheet
-
csv_string = PacBio::Worksheet.new.create_csv_from_batch(@batch)
-
send_data csv_string, :type => "text/plain",
-
:filename=>"batch_#{@batch.id}_worksheet.csv",
-
:disposition => 'attachment'
-
end
-
-
-
1
def find_batch_by_barcode
-
batch_id = LabEvent.find_by_barcode(params[:id])
-
if batch_id == 0
-
@batch_error = "Batch id not found."
-
render :action => "batch_error", :format => :xml
-
return
-
else
-
@batch = Batch.find(batch_id)
-
render :action => "show", :format => :xml
-
end
-
end
-
-
1
private
-
1
def pipeline_error_on_batch_creation(message)
-
respond_to do |format|
-
flash[:error] = message
-
format.html { redirect_to pipeline_url(@pipeline) }
-
end
-
return
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2013 Genome Research Ltd.
-
1
require 'formtastic'
-
-
1
class BulkSubmissionsController < ApplicationController
-
-
1
def index
-
redirect_to :action => "new"
-
end
-
-
1
def new
-
# default action - shows file upload form
-
@bulk_submission = BulkSubmission.new
-
end
-
-
1
def create
-
begin
-
@bulk_submission = BulkSubmission.new(:spreadsheet => params.fetch(:bulk_submission, {})[:spreadsheet])
-
-
if @bulk_submission.valid?
-
flash[:notice] = "File was processed successfully"
-
sub_ids,@sub_details = @bulk_submission.completed_submissions
-
@these_subs = Submission.find(sub_ids)
-
#Submission.all(:conditions => ["created_at > :lastminute", { :lastminute => Time.now - 1.day}])
-
-
else
-
#flash[:error] = "There was a problem with your upload"
-
# apparently this should redirect_to rather than render, but then the errors don't show up properly
-
render :action => "new"
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
flash.now[:error] = exception.message
-
render :action => "new"
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class DocumentsController < ApplicationController
-
1
def show
-
@document = Document.find(params[:id])
-
send_data @document.current_data, :filename => @document.filename, :type => @document.content_type, :disposition => 'inline'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class EventsController < ApplicationController
-
-
1
def new
-
@event = Event.new
-
end
-
-
1
def create
-
# Compatible with NPG
-
-
params[:event].delete(:key)
-
-
@event = Event.create(params[:event])
-
-
unless @event.eventful.nil?
-
@event.eventful.save
-
end
-
-
respond_to do |format|
-
# I know this looks crazy, but it appears to be explicitly tested for
-
# Unfortunately there is no comment as to why.
-
format.html { render :xml => @event.to_xml }
-
format.xml { render :xml => @event.to_xml }
-
format.json { render :json => @event.to_json }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class GelsController < ApplicationController
-
1
before_filter :slf_gel_login_required
-
-
1
def index
-
# TODO: if a plate has a working dilution plate and it has a gel dilution plate, display:
-
@gel_plates = GelDilutionPlate.paginate(:page => params[:page], :order => 'id DESC')
-
@plates = @gel_plates.map(&:stock_plate).compact
-
end
-
-
1
def find
-
end
-
-
1
def lookup
-
@plate = Plate.find_by_barcode_and_barcode_prefix_id(params[:barcode], BarcodePrefix.find_by_prefix(Plate.prefix))
-
if !@plate
-
flash[:error] = "plate not found"
-
render :action => :find
-
return
-
end
-
-
render :action => :show
-
end
-
-
1
def show
-
@plate = Plate.find(params[:id])
-
end
-
-
1
def update
-
ActiveRecord::Base.transaction do
-
params[:wells].keys.each do |well_id|
-
well = Well.find(well_id)
-
well.well_attribute.update_attributes!( :gel_pass => params[:wells][well_id][:qc_state])
-
well.events.create_gel_qc!(params[:wells][well_id][:qc_state], current_user)
-
end
-
Plate.find(params[:id]).events.create_gel_qc!('', current_user)
-
end
-
flash[:notice] = "Gel results for plate updated"
-
redirect_to :action => :index
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
# Provides a simple endpoint for monitoring server status
-
1
class HealthController < ApplicationController
-
-
1
before_filter :login_required, :except => [ :index ]
-
-
1
def index
-
@monitor = Health.new
-
-
respond_to do |format|
-
format.json { render :json => @monitor, :status => @monitor.status }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2016 Genome Research Ltd.
-
1
class HomesController < ApplicationController
-
1
before_filter :login_required
-
-
1
def show
-
@links = configatron.fetch(:external_applications,[])
-
@pipelines = current_user.pipelines.active
-
@latest_batches = current_user.batches.latest_first.limit(10).includes(:pipeline)
-
@assigned_batches = current_user.batches.latest_first.where('assignee_id != user_id').limit(10).includes(:pipeline)
-
@submissions = current_user.submissions.latest_first.limit(10)
-
@studies = current_user.interesting_studies.newest_first.limit(10)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2014,2015 Genome Research Ltd.
-
1
class LabSearchesController < ApplicationController
-
1
include SearchBehaviour
-
1
alias_method(:new, :search)
-
-
1
def index
-
redirect_to :action => :new
-
end
-
-
1
private
-
-
1
SEARCHABLE_CLASSES = [ Batch, Asset ]
-
1
def searchable_classes
-
SEARCHABLE_CLASSES
-
end
-
-
1
def extended
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class LabwhereReceptionsController < ApplicationController
-
-
1
before_filter :login_required, :except => [:index,:create]
-
-
1
def index
-
@labwhere_reception = LabwhereReception.new(params[:user_code],params[:location_barcode],params[:location_id],[])
-
end
-
-
1
def create
-
#user_barcode,location_barcode,asset_barcodes
-
input = params[:labwhere_reception]||{}
-
barcodes = input[:barcodes].try(:values)||[]
-
-
lwr = LabwhereReception.new(input[:user_code],input[:location_barcode],input[:location_id],barcodes)
-
if lwr.save
-
flash[:notice] = "Locations updated!"
-
else
-
flash[:error] = lwr.errors.join('; ')
-
end
-
redirect_to labwhere_receptions_path, :location_id => params[:location_id]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class LocationsController < ApplicationController
-
1
before_filter :find_location_by_id, :only => [:show, :edit, :update, :destroy]
-
-
1
def index
-
@locations = Location.find(:all)
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @locations }
-
end
-
end
-
-
1
def show
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @location }
-
end
-
end
-
-
1
def new
-
@location = Location.new
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @location }
-
end
-
end
-
-
1
def edit
-
end
-
-
1
def create
-
@location = Location.new(params[:location])
-
-
respond_to do |format|
-
if @location.save
-
flash[:notice] = 'Location was successfully created.'
-
format.html { redirect_to(@location) }
-
format.xml { render :xml => @location, :status => :created, :location => @location }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @location.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @location.update_attributes(params[:location])
-
flash[:notice] = 'Location was successfully updated.'
-
format.html { redirect_to(@location) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @location.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@location.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(locations_url) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def find_location_by_id
-
@location = Location.find(params[:id])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class MachineBarcodesController < ApplicationController
-
-
1
def show
-
asset = Asset.with_machine_barcode(params[:id]).first
-
summary = asset.present? ? asset.summary_hash : {}
-
status = asset.present? ? 200 : 404
-
respond_to do |format|
-
format.json { render :json => summary, :status => status }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.
-
class NpgActions::AssetsController < ApplicationController
-
before_filter :login_required, :except => [ :pass, :fail ]
-
before_filter :find_asset, :only => [ :pass, :fail ]
-
before_filter :find_request, :only => [ :pass, :fail ]
-
-
rescue_from(ActiveRecord::RecordNotFound, :with => :rescue_error)
-
-
before_filter :npg_action_invalid?, :only => [ :pass, :fail ]
-
-
-
before_filter :xml_valid?, :only => [:pass, :fail]
-
-
-
XmlInvalid = Class.new(StandardError)
-
rescue_from(XmlInvalid, :with => :rescue_error)
-
-
NPGActionInvalid = Class.new(StandardError)
-
rescue_from(NPGActionInvalid, :with => :rescue_error_internal_server_error)
-
-
#this procedure build a procedure called "state". In this casa: pass and fail.
-
def self.construct_action_for_qc_state(state)
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{state}
-
begin
-
ActiveRecord::Base.transaction do
-
@asset.set_qc_state('#{state}ed')
-
@asset.events.create_#{state}!(params[:qc_information][:message] || 'No reason given')
-
request = @asset.source_request
-
-
batch = request.batch
-
raise ActiveRecord::RecordNotFound, "Unable to find a batch for the Request" if (batch.nil?)
-
-
message = "#{state}ed manual QC".capitalize
-
EventSender.send_#{state}_event(request.id, "", message, "","npg", :need_to_know_exceptions => true)
-
-
batch.npg_set_state if ('#{state}' == 'pass')
-
-
end
-
end
-
-
respond_to do |format|
-
format.xml { render :file => 'assets/show'}
-
end
-
end
-
}, __FILE__, line)
-
end
-
-
construct_action_for_qc_state("pass")
-
construct_action_for_qc_state("fail")
-
-
1
private
-
-
1
def find_asset
-
@asset ||= Asset.find(params[:asset_id])
-
end
-
-
1
def find_request
-
@asset ||= Asset.find(params[:asset_id])
-
if ((@asset.has_many_requests?) || (@asset.source_request.nil?))
-
raise ActiveRecord::RecordNotFound, "Unable to find a request for Lane: #{params[:id]}"
-
end
-
end
-
-
1
def xml_valid?
-
raise XmlInvalid, "XML invalid" if params[:qc_information].nil?
-
end
-
-
1
def npg_action_invalid?
-
@asset ||= Asset.find(params[:asset_id])
-
request = @asset.source_request
-
npg_events = Event.npg_events(request.id)
-
raise NPGActionInvalid, "NPG user run this action. Please, contact USG" if npg_events.size > 0
-
end
-
-
1
def rescue_error(exception)
-
respond_to do |format|
-
format.xml { render :xml => "<error><message>#{exception.message}</message></error>", :status => "404" }
-
end
-
end
-
-
1
def rescue_error_internal_server_error(exception)
-
respond_to do |format|
-
format.xml { render :xml => "<error><message>#{exception.message}</message></error>", :status => "500" }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
1
class OrdersController < ApplicationController
-
1
def destroy
-
# Check for ajax request...
-
if request.xhr?
-
Order.find(params[:id]).destroy
-
head :accepted
-
end
-
end
-
-
1
def update
-
@order = Order.find(params[:id])
-
@order.add_comment(params[:comments], current_user) unless params[:comments].nil?
-
-
redirect_to @order.submission
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
class PicoDilutionsController < ApplicationController
-
1
before_filter :login_required, :except => [:index]
-
-
1
def index
-
pico_dilutions = DilutionPlate.with_pico_children.paginate :page => params[:page], :order => 'id DESC', :per_page => 500
-
pico_dilutions_hash = PicoDilutionPlate.index_to_hash(pico_dilutions)
-
-
respond_to do |format|
-
format.xml { render :xml => pico_dilutions_hash, :root => 'records' }
-
format.json { render :json => pico_dilutions_hash }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.
-
1
class PicoSetResultsController < ApplicationController
-
1
before_filter :login_required, :except => [:create]
-
-
# TODO This should be an update method not create
-
# TODO Refactor. Create an object for pico_set_result
-
1
def create
-
-
pico_set_result = params[:pico_set_result]
-
-
if pico_set_result
-
pico_assay_plate = PicoAssayPlate.find_from_machine_barcode(pico_set_result[:assay_barcode])
-
end
-
-
respond_to do |format|
-
if pico_assay_plate.nil?
-
flash[:error] = "Barcode #{pico_set_result[:assay_barcode]} not found"
-
format.xml { render :xml => flash.to_xml, :status => :not_found }
-
format.json { render :json => flash.to_json, :status => :not_found }
-
else
-
# create method call using pico_assay_plate[:state] and use send for 2 method calls
-
if pico_assay_plate.upload_pico_results(pico_set_result[:state], pico_set_result[:failure_reason], pico_set_result[:wells])
-
flash[:notice] = 'Updated concentrations'
-
format.xml { render :xml => flash.to_xml, :status => :ok }
-
format.json { render :json => flash.to_json, :status => :ok }
-
else
-
flash[:error] = "Couldn't upload results"
-
format.xml { render :xml => flash.to_xml, :status => :unprocessable_entity }
-
format.json { render :json => flash.to_json, :status => :unprocessable_entity }
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# NOTE[xxx]: This controller may not be required but is here to support the Javascript used in the
-
# library prep pipeline.
-
1
class Pipelines::AssetsController < ApplicationController
-
1
def new
-
@asset, @family = Asset.new, Family.find(params[:family])
-
render :partial => 'descriptor', :locals => { :field => Descriptor.new, :field_number => params[:id] }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class PipelinesController < ApplicationController
-
1
before_filter :find_pipeline_by_id, :only => [ :show, :setup_inbox,
-
:set_inbox, :training_batch, :activate, :deactivate, :destroy, :batches]
-
-
1
before_filter :lab_manager_login_required, :only => [:update_priority,:deactivate,:activate]
-
-
1
after_filter :set_cache_disabled!, :only => [:show]
-
-
1
def index
-
@pipelines = Pipeline.active.internally_managed.alphabetical.all
-
@grouping = @pipelines.inject(Hash.new { |h,k| h[k] = [] }) { |h,p| h[p.group_name] << p ; h }
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @pipelines.to_xml}
-
end
-
end
-
-
1
def show
-
self.expires_now
-
@show_held_requests = (params[:view] == 'all')
-
@current_page = params[:page]
-
-
@pending_batches = @pipeline.batches.pending_for_ui.includes_for_ui
-
@batches_in_progress = @pipeline.batches.in_progress_for_ui.includes_for_ui
-
@completed_batches = @pipeline.batches.completed_for_ui.includes_for_ui
-
@released_batches = @pipeline.batches.released_for_ui.includes_for_ui
-
@failed_batches = @pipeline.batches.failed_for_ui.includes_for_ui
-
-
@batches = @last_5_batches = @pipeline.batches.latest_first.includes_for_ui
-
-
unless @pipeline.qc?
-
@information_types = @pipeline.request_information_types
-
@requests_waiting = @pipeline.requests.inbox(@show_held_requests, @current_page, :count)
-
-
if @pipeline.group_by_parent?
-
# We use the inbox presenter
-
@inbox_presenter = Presenters::GroupedPipelineInboxPresenter.new(@pipeline,current_user,@show_held_requests)
-
elsif @pipeline.group_by_submission?
-
requests = @pipeline.requests.inbox(@show_held_requests,@current_page)
-
@grouped_requests = requests.group_by(&:submission_id)
-
@requests_comment_count = Comment.counts_for(requests)
-
@assets_comment_count = Comment.counts_for(requests.map(&:asset))
-
else
-
@requests = @pipeline.requests.inbox(@show_held_requests,@current_page)
-
@requests_comment_count = Comment.counts_for(@requests)
-
@assets_comment_count = Comment.counts_for(@requests.map(&:asset))
-
end
-
end
-
end
-
-
1
def setup_inbox
-
@controls = []
-
end
-
-
1
def set_inbox
-
unless params[:controls].blank?
-
add_controls(@pipeline, params[:controls])
-
end
-
-
if @pipeline.save
-
flash[:notice] = "Updated pipeline controls"
-
redirect_to pipeline_url(@pipeline)
-
else
-
flash[:notice] = "Failed to set pipeline controls"
-
render :action => "setup_inbox", :id => @pipeline.id
-
end
-
end
-
-
1
def training_batch
-
@controls = @pipeline.controls
-
end
-
-
-
1
before_filter :prepare_batch_and_pipeline, :only => [ :summary, :finish ]
-
1
def prepare_batch_and_pipeline
-
@batch = Batch.find(params[:id])
-
@pipeline = @batch.pipeline
-
end
-
1
private :prepare_batch_and_pipeline
-
-
1
def summary
-
-
end
-
-
1
def finish
-
@batch.complete!(current_user)
-
rescue ActiveRecord::RecordInvalid => exception
-
flash[:error] = exception.record.errors.full_messages
-
redirect_to(url_for(:controller => 'batches', :action => 'show', :id => @batch.id))
-
end
-
-
1
def release
-
ActiveRecord::Base.transaction do
-
@batch = Batch.find(params[:id])
-
@batch.release!(current_user)
-
end
-
-
flash[:notice] = 'Batch released!'
-
redirect_to :controller => "batches", :action => "show", :id => @batch.id
-
end
-
-
1
def activate
-
@pipeline.active = true
-
if @pipeline.save
-
flash[:notice] = "Pipeline activated"
-
redirect_to pipelines_path
-
else
-
flash[:notice] = "Failed to activate pipeline"
-
redirect_to pipeline_path(@pipeline)
-
end
-
end
-
-
1
def deactivate
-
@pipeline.active = false
-
if @pipeline.save
-
flash[:notice] = "Pipeline deactivated"
-
redirect_to pipelines_path
-
else
-
flash[:notice] = "Failed to deactivate pipeline"
-
redirect_to pipeline_path(@pipeline)
-
end
-
end
-
-
1
def batches
-
@batches = @pipeline.batches.paginate :page => params[:page], :order => 'id DESC'
-
end
-
-
# to modify when next_request will be ready
-
1
def update_priority
-
request = Request.find(params[:request_id])
-
ActiveRecord::Base.transaction do
-
request.update_priority
-
render :text => '', :layout => false
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
render :text => '', :layout => false, :status => :unprocessable_entity
-
end
-
-
1
private
-
1
def find_pipeline_by_id
-
@pipeline = Pipeline.find(params["id"])
-
end
-
-
1
def add_controls(pipeline, controls)
-
controls.each do |control|
-
values = control.split(",")
-
unless Control.exists?(:item_id => values.last, :pipeline_id => pipeline.id)
-
pipeline.controls.create(:name => values.first, :item_id => values.last, :pipeline_id => pipeline.id)
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
class PlateTemplatesController < ApplicationController
-
1
before_filter :slf_manager_login_required
-
1
def index
-
@patterns = PlateTemplate.paginate(:per_page => 50, :page => params[:page])
-
end
-
-
1
def new
-
@plate_rows = params[:rows].to_i
-
@plate_cols = params[:cols].to_i
-
if @plate_rows == 0
-
@plate_rows = Map::Coordinate.plate_length(96)
-
end
-
if @plate_cols == 0
-
@plate_cols = Map::Coordinate.plate_width(96)
-
end
-
-
respond_to do |format|
-
format.html # new.html.erb
-
end
-
end
-
-
1
def create
-
if params[:name].blank?
-
flash[:error] = "Please enter a name"
-
redirect_to new_plate_template_path
-
return
-
end
-
-
pattern = PlateTemplate.new
-
pattern.update_params!(:name => params[:name], :user_id=>current_user.id, :wells => params[:empty_well],:control_well => params[:control_well], :rows => params[:rows], :cols => params[:cols])
-
flash[:notice] = "Template saved"
-
redirect_to plate_templates_path
-
end
-
-
1
def edit
-
@pattern = PlateTemplate.find(params[:id])
-
@plate_rows = Map::Coordinate.plate_length(@pattern.size)
-
@plate_cols = Map::Coordinate.plate_width(@pattern.size)
-
end
-
-
1
def update
-
pattern = PlateTemplate.find(params[:id])
-
pattern.update_params!(:name => params[:name], :user_id=>current_user.id, :control_well => params[:control_well], :wells => params[:empty_well], :rows => params[:rows], :cols => params[:cols])
-
flash[:notice] = "Template updated"
-
redirect_to plate_templates_path
-
end
-
-
1
def destroy
-
pattern = PlateTemplate.find(params[:id])
-
pattern.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(plate_templates_path) }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class PlatesController < ApplicationController
-
1
before_filter :login_required, :except => [:upload_pico_results, :fluidigm_file]
-
-
1
def new
-
@plate_creators = Plate::Creator.all(:order => 'name ASC')
-
@barcode_printers = BarcodePrinterType.find_by_name("96 Well Plate").barcode_printers
-
@barcode_printers = BarcodePrinter.find(:all, :order => "name asc") if @barcode_printers.blank?
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @plate }
-
format.json { render :json => @plate }
-
end
-
end
-
-
1
def show
-
@plate = Plate.find(params[:id])
-
end
-
-
-
1
def create
-
ActiveRecord::Base.transaction do
-
plate_creator = Plate::Creator.find(params[:plates][:creator_id])
-
barcode_printer = BarcodePrinter.find(params[:plates][:barcode_printer])
-
source_plate_barcodes = params[:plates][:source_plates]
-
-
user_barcode = Barcode.barcode_to_human(params[:plates][:user_barcode])
-
scanned_user = User.find_by_barcode(user_barcode) if user_barcode
-
-
respond_to do |format|
-
if scanned_user.nil?
-
flash[:error] = 'Please scan your user barcode'
-
format.html { redirect_to(new_plate_path) }
-
elsif plate_creator.execute(source_plate_barcodes, barcode_printer, scanned_user, Plate::CreatorParameters.new(params[:plates]))
-
flash[:notice] = 'Created plates and printed barcodes'
-
format.html { redirect_to(new_plate_path) }
-
else
-
flash[:error] = 'Failed to create plates'
-
format.html { redirect_to(new_plate_path) }
-
end
-
end
-
end
-
rescue Plate::Creator::PlateCreationError, ActiveRecord::RecordNotFound => e
-
respond_to do |format|
-
flash[:error] = e.message
-
format.html { redirect_to(new_plate_path) }
-
end
-
end
-
-
1
def to_sample_tubes
-
@locations = Location.all
-
@barcode_printers = BarcodePrinter.all
-
@studies = Study.all.sort{ |a,b| a.name <=> b.name }
-
end
-
-
1
def create_sample_tubes
-
location = Location.find(params[:plates][:destination_freezer])
-
barcode_printer = BarcodePrinter.find(params[:plates][:barcode_printer])
-
plates = Plate.plates_from_scanned_plates_and_typed_plate_ids(params[:plates][:source_plates])
-
study = Study.find(params[:plates][:study])
-
-
respond_to do |format|
-
if asset_group = Plate.create_sample_tubes_asset_group_and_print_barcodes(plates, barcode_printer, location, study)
-
flash[:notice] = 'Created tubes and printed barcodes'
-
# makes request properties partial show
-
@current_user.workflow = Submission::Workflow.find_by_key("short_read_sequencing")
-
@current_user.save!
-
format.html { redirect_to(new_submission_path(:study_id=>asset_group.study.id)) }
-
format.xml { render :xml => asset_group, :status => :created}
-
format.json { render :json => asset_group, :status => :created}
-
else
-
flash[:error] = 'Failed to create sample tubes'
-
format.html { redirect_to(to_sample_tubes_plates_path) }
-
format.xml { render :xml => flash.to_xml, :status => :unprocessable_entity }
-
format.json { render :json => flash.to_json, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def fluidigm_file
-
if logged_in?
-
@plate = Plate.find(params[:id])
-
@parents = @plate.parents
-
respond_to do |format|
-
format.csv { render :csv => @plate, :content_type => "text/csv" }
-
end
-
end
-
end
-
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Projects::Workflows::ItemsController < ApplicationController
-
-
1
def index
-
@workflow = Submission::Workflow.find(params[:workflow_id])
-
@project = Project.find(params[:project_id])
-
-
submissions = @project.submissions.select { |s| s.workflow == @workflow }
-
@items = submissions.map { |s| s.items }.flatten.uniq
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @items.to_xml }
-
end
-
end
-
-
1
def print
-
@workflow = Submission::Workflow.find(params[:workflow_id])
-
@project = Project.find(params[:project_id])
-
printables = []
-
params[:printable].each do |key, value|
-
item = Item.find(key)
-
request = item.requests.detect{ |r| r.request_type.key == "receive_sample" }
-
unless request.nil?
-
printables.push PrintBarcode::Label.new({ :number => request.id, :project => "#{item.id}_#{item.name.gsub("_"," ")}", :suffix => "" })
-
end
-
end
-
if !printables.empty?
-
BarcodePrinter.print(printables, params[:printer])
-
end
-
flash[:notice] = "Your labels have been sent to printer #{params[:printer]}."
-
redirect_to project_workflow_items_path(@project, @workflow)
-
rescue SOAP::FaultError
-
flash[:warning] = "There is a problem with the selected printer. Please report it to Systems."
-
redirect_to project_workflow_items_path(@project, @workflow)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
class ProjectsController < ApplicationController
-
1
before_filter :login_required
-
1
before_filter :set_variables_for_project, :only => [:show, :edit, :update, :destroy, :studies]
-
#TODO: before_filter :redirect_if_not_owner_or_admin, :only => [:create, :update, :destroy, :edit, :new]
-
-
1
def index
-
@projects = Project.alphabetical.page(params[:page])
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => Project.all(:order => 'name ASC') }
-
format.json { render :json => Project.all(:order => 'name ASC') }
-
end
-
end
-
-
1
def show
-
if current_user.present?
-
@workflow = current_user.workflow
-
# TODO[xxx]: filtered the project based on user workflow
-
end
-
-
respond_to do |format|
-
format.html
-
format.xml
-
format.json { render :json => @project }
-
end
-
end
-
-
1
def new
-
@project = Project.new
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @project }
-
format.json { render :json => @project }
-
end
-
end
-
-
1
def edit
-
@project = Project.find(params[:id])
-
@users = User.all
-
end
-
-
1
def create
-
# TODO[5002667]: All of this code should be in a before_create/after_create callback in the Project model ...
-
@project = Project.new(params[:project])
-
@project.save!
-
-
current_user.has_role('manager', @project)
-
-
# Creates an event when a new Project is created
-
EventFactory.new_project(@project, current_user)
-
-
# TODO[5002667]: ... to here.
-
-
flash[:notice] = "Your project has been created"
-
respond_to do |format|
-
format.html { redirect_to project_path(@project) }
-
format.xml { render :xml => @project, :status => :created, :location => @project }
-
format.json { render :json => @project, :status => :created, :location => @project }
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
flash.now[:error] = "Problems creating your new project"
-
respond_to do |format|
-
format.html {
-
render :action => "new"
-
}
-
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
-
format.json { render :json => @project.errors, :status => :unprocessable_entity }
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @project.update_attributes(params[:project])
-
flash[:notice] = 'Project was successfully updated.'
-
format.html { redirect_to(@project) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @project.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@project.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(projects_url) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def related_studies
-
@project = Project.find(params[:id])
-
@studies = @project.studies
-
-
respond_to do |format|
-
format.html
-
format.xml
-
end
-
end
-
-
1
def collaborators
-
@project = Project.find(params[:id])
-
#@all_roles = Role.all(:select => " distinct `name`")
-
@all_roles = Role.all(:conditions => {:name => ["owner","follower","manager"]},:select => " distinct `name`")
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
@users = User.all(:order => :first_name)
-
end
-
-
1
def follow
-
@project = Project.find(params[:id])
-
if current_user.has_role? 'follower', @project
-
current_user.has_no_role 'follower', @project
-
flash[:notice] = "You have stopped following the '#{@project.name}' project."
-
else
-
current_user.has_role 'follower', @project
-
flash[:notice] = "You are now following the '#{@project.name}' project."
-
end
-
redirect_to project_path(@project)
-
end
-
-
1
def grant_role
-
@user = User.find(params[:role][:user])
-
@project = Project.find(params[:id])
-
@role = Role.find_by_name(params[:role][:authorizable_type])
-
-
if request.xhr?
-
if params[:role]
-
@user.has_role(params[:role][:authorizable_type].to_s, @project)
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
flash[:notice] = "Role added"
-
render :partial => "roles", :status => 200
-
else
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
flash[:error] = "A problem occurred while adding the role"
-
render :partial => "roles", :status => 500
-
end
-
else
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
flash[:error] = "A problem occurred while adding the role"
-
render :partial => "roles", :status => 401
-
end
-
end
-
-
1
def remove_role
-
@user = User.find(params[:role][:user])
-
@project = Project.find(params[:id])
-
@role = Role.find_by_name(params[:role][:authorizable_type])
-
-
if request.xhr?
-
if params[:role]
-
@user.has_no_role(params[:role][:authorizable_type].to_s, @project)
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
flash[:error] = "Role was removed"
-
render :partial => "roles", :status => 200
-
else
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
flash[:error] = "A problem occurred while removing the role"
-
render :partial => "roles", :status => 500
-
end
-
else
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @project.id, :authorizable_type => "Project"})
-
flash[:error] = "A problem occurred while removing the role"
-
render :partial => "roles", :status => 401
-
end
-
end
-
-
1
private
-
1
def set_variables_for_project
-
@project = Project.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
class QcReportsController < ApplicationController
-
-
1
before_filter :login_required
-
1
before_filter :check_required, :only => :create
-
-
1
def index
-
# Build a conditions hash of acceptable parameters, ignoring those that are blank
-
-
@qc_reports = QcReport.for_report_page(conditions).paginate(:page => params[:page],:include => [:study,:product])
-
@qc_report = QcReport.new(:exclude_existing => true,:study_id => params[:study_id])
-
@studies = Study.alphabetical.for_listing.all.map {|s| [s.name,s.id] }
-
@states = QcReport.available_states.map {|s| [s.humanize,s] }
-
-
@all_products = Product.alphabetical.all.map {|product| [product.display_name,product.id]}
-
@active_products = Product.with_stock_report.active.alphabetical.all.map {|product| [product.display_name,product.id]}
-
end
-
-
1
def create
-
-
study = Study.find_by_id(params[:qc_report][:study_id])
-
exclude_existing = params[:qc_report][:exclude_existing] == "1"
-
-
qc_report = QcReport.new(:study=>study, :product_criteria => @product.stock_criteria, :exclude_existing => exclude_existing)
-
-
if qc_report.save
-
flash[:notice] = 'Your report has been requested and will be presented on this page when complete.'
-
redirect_to qc_report
-
else # We failed to save
-
error_messages = qc_report.errors.full_messages.join('; ')
-
flash[:error] = "Failed to create report: #{error_messages}"
-
redirect_to :back
-
end
-
-
-
end
-
-
# On form submit of a qc_file. Strictly speaking this should be an update action
-
# on the qc_report itself. However we don't want to force the user to extract
-
# the report identifier from the file.
-
1
def qc_file
-
qc_file = params[:qc_report_file]
-
overide_qc = params[:overide_qc_decision] == "1"
-
file = QcReport::File.new(qc_file,overide_qc,qc_file.original_filename,qc_file.content_type)
-
if file.process
-
redirect_to file.qc_report
-
else
-
flash[:error] = "Failed to read report: #{file.errors.join('; ')}"
-
redirect_to :back
-
end
-
end
-
-
1
def show
-
qc_report = QcReport.find_by_report_identifier(params[:id])
-
queue_count = qc_report.queued? ? Delayed::Job.count : 0
-
@report_presenter = Presenters::QcReportPresenter.new(qc_report,queue_count)
-
-
respond_to do |format|
-
format.html
-
-
format.csv do
-
file = nil
-
begin
-
file = Tempfile.new(@report_presenter.filename)
-
@report_presenter.to_csv(file)
-
file.flush
-
ensure
-
file.close unless file.nil?
-
end
-
send_file file.path, :content_type => "text/csv", :filename => @report_presenter.filename
-
end if qc_report.available?
-
end
-
end
-
-
1
private
-
-
1
def check_required
-
return fail('No report options were provided') unless params[:qc_report].present?
-
return fail('You must select a product') if params[:qc_report][:product_id].nil?
-
@product = Product.find_by_id(params[:qc_report][:product_id])
-
return fail('Could not find product') if @product.nil?
-
return fail("#{product.name} is inactive") if @product.deprecated?
-
return fail("#{product.name} does not have any stock criteria set") if @product.stock_criteria.nil?
-
true
-
end
-
-
1
def fail(message)
-
redirect_to :back, :alert => message
-
false
-
end
-
-
1
def conditions
-
conds = {}
-
conds[:study_id] = params[:study_id] if params[:study_id].present?
-
conds[:product_criteria] = {:product_id => params[:product_id]} if params[:product_id].present?
-
conds[:state] = params[:state] if params[:state].present?
-
conds
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2014,2015 Genome Research Ltd.
-
1
class ReceptionsController < ApplicationController
-
1
before_filter :find_asset_by_id, :only => [:print, :snp_register]
-
-
1
def index
-
@num_text_boxes = 10
-
end
-
-
1
def snp_import
-
end
-
-
1
def print
-
@assets = [@asset]
-
end
-
-
1
def receive_barcode
-
barcodes = params[:barcode]
-
@new_plates = []
-
@errors = []
-
@assets = []
-
-
all_barcodes_blank = true
-
-
barcodes.each do |index,barcode_ws|
-
-
# We don't perform strip! as this results in modification of the parameters themselves, which affects logging and
-
# exception notification. This can hinder investigation of any issues, as it changes apparent user input.
-
barcode = barcode_ws.strip
-
if barcode.blank?
-
next
-
else
-
all_barcodes_blank = false
-
end
-
unless barcode.size == 13
-
@errors << "Invalid barcode size for: #{barcode}"
-
next
-
end
-
if Barcode.check_EAN(barcode) == false
-
@errors << "Wrong barcode checksum for barcode: #{barcode}"
-
next
-
end
-
prefix, id, checksum = Barcode.split_barcode(barcode)
-
-
case 'Plate'
-
when 'LibraryTube' then @asset = LibraryTube.find_by_barcode(id)
-
when 'MultiplexedLibraryTube' then @asset = MultiplexedLibraryTube.find_by_barcode(id)
-
when 'PulldownMultiplexedLibraryTube' then @asset = PulldownMultiplexedLibraryTube.find_by_barcode(id)
-
when 'Plate' then @asset = Asset.find_from_machine_barcode(barcode)
-
when 'SampleTube' then @asset = SampleTube.find_by_barcode(id)
-
else
-
@asset = Asset.find_from_machine_barcode(barcode)
-
end
-
-
-
if @asset.nil?
-
@generic_asset = Asset.find_by_barcode(id)
-
if @generic_asset.nil?
-
@errors << "Sample with barcode #{barcode} not found"
-
else
-
@errors << "Incorrect type for #{barcode} is a #{@generic_asset.sti_type} not a #{params[:type][:id]}"
-
end
-
next
-
else
-
@assets << @asset
-
end
-
end
-
-
if all_barcodes_blank
-
@errors << "No barcodes have been entered or scanned"
-
end
-
-
if @errors.size > 0
-
respond_to do |format|
-
flash[:error] = "Error with scanned samples: #{@errors.join('. ')}"
-
format.html { render :action => :index }
-
format.xml { render :xml => @errors, :status => :unprocessable_entity }
-
format.json { render :json => @errors, :status => :unprocessable_entity }
-
end
-
else
-
respond_to do |format|
-
format.html
-
format.xml { head :ok }
-
format.json { head :ok }
-
end
-
end
-
end
-
-
1
def confirm_reception
-
ActiveRecord::Base.transaction do
-
location = Location.find(params[:asset][:location_id])
-
assets = params[:asset_id]
-
@errors = []
-
asset_count = 0
-
-
assets.each do |index,asset_id|
-
begin
-
@asset = Asset.find(asset_id)
-
@asset.update_attributes(params[:asset])
-
asset_count += 1
-
@asset.events.create_scanned_into_lab!(location)
-
rescue
-
@errors << "Sample not found with asset ID #{asset_id}"
-
end
-
end
-
-
if @errors.size > 0
-
respond_to do |format|
-
flash[:error] = "Assets not found"
-
format.html { render :action => "reception" }
-
format.xml { render :xml => @errors, :status => :unprocessable_entity }
-
format.json { render :json => @errors, :status => :unprocessable_entity }
-
end
-
else
-
respond_to do |format|
-
flash[:notice] = "Successfully updated #{asset_count} samples"
-
format.html { render :action => "reception" }
-
format.xml { head :ok }
-
format.json { head :ok }
-
end
-
end
-
end
-
end
-
-
1
def receive_snp_barcode
-
barcodes = params[:barcodes]
-
@snp_plates = []
-
@errors = []
-
-
barcodes.scan(/\d+/).each do |plate_barcode|
-
plate = Plate.find_by_barcode(plate_barcode)
-
if plate.nil?
-
@snp_plates << plate_barcode
-
else
-
@snp_plates << plate
-
end
-
end
-
-
if @errors.size > 0
-
respond_to do |format|
-
format.html { render :action => "snp_import" }
-
format.xml { render :xml => @errors, :status => :unprocessable_entity }
-
format.json { render :json => @errors, :status => :unprocessable_entity }
-
end
-
else
-
respond_to do |format|
-
format.html
-
format.xml { head :ok }
-
format.json { head :ok }
-
end
-
end
-
end
-
-
1
def import_from_snp
-
ActiveRecord::Base.transaction do
-
respond_to do |format|
-
if Plate.create_plates_with_barcodes(params)
-
flash[:notice] = "Plates queued to be imported"
-
format.html { redirect_to :action => "snp_import" }
-
format.xml { head :ok }
-
format.json { head :ok }
-
else
-
flash[:errors] = "Plates could not be created"
-
format.html { render :action => "snp_import" }
-
format.xml { render :xml => @errors, :status => :unprocessable_entity }
-
format.json { render :json => @errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
end
-
-
1
def find_asset_by_id
-
@asset = Asset.find(params[:id])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ReferenceGenomesController < ApplicationController
-
1
before_filter :admin_login_required, :only => [:new, :create, :edit, :update, :destroy]
-
-
1
def index
-
@reference_genomes = ReferenceGenome.all(:order => "name ASC")
-
respond_to do |format|
-
format.html # index.html.erb
-
format.xml { render :xml => @reference_genomes }
-
end
-
end
-
-
# GET /reference_genomes/1
-
# GET /reference_genomes/1.xml
-
1
def show
-
@reference_genome = ReferenceGenome.find(params[:id])
-
-
respond_to do |format|
-
format.html # show.html.erb
-
format.xml { render :xml => @reference_genome }
-
end
-
end
-
-
# GET /reference_genomes/new
-
# GET /reference_genomes/new.xml
-
1
def new
-
@reference_genome = ReferenceGenome.new
-
-
respond_to do |format|
-
format.html # new.html.erb
-
format.xml { render :xml => @reference_genome }
-
end
-
end
-
-
# GET /reference_genomes/1/edit
-
1
def edit
-
@reference_genome = ReferenceGenome.find(params[:id])
-
end
-
-
# POST /reference_genomes
-
# POST /reference_genomes.xml
-
1
def create
-
@reference_genome = ReferenceGenome.new(params[:reference_genome])
-
-
respond_to do |format|
-
if @reference_genome.save
-
format.html { redirect_to(@reference_genome, :notice => 'Reference genome was successfully created.') }
-
format.xml { render :xml => @reference_genome, :status => :created, :location => @reference_genome }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @reference_genome.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
# PUT /reference_genomes/1
-
# PUT /reference_genomes/1.xml
-
1
def update
-
@reference_genome = ReferenceGenome.find(params[:id])
-
-
respond_to do |format|
-
if @reference_genome.update_attributes(params[:reference_genome])
-
format.html { redirect_to(@reference_genome, :notice => 'Reference genome was successfully updated.') }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @reference_genome.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /reference_genomes/1
-
# DELETE /reference_genomes/1.xml
-
1
def destroy
-
@reference_genome = ReferenceGenome.find(params[:id])
-
@reference_genome.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(reference_genomes_url) }
-
format.xml { head :ok }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Requests::CommentsController < ApplicationController
-
1
before_filter :discover_request
-
-
1
def index
-
@comments = @request.comments.order("created_at ASC")
-
if request.xhr?
-
render :partial => "simple_list", :locals => { :descriptions => @comments.pluck(:description) }
-
else
-
# Perform default
-
end
-
end
-
-
1
def create
-
@request.comments.create(:description => params[:comment], :user_id => current_user.id)
-
@comments = @request.comments
-
render :partial => "list", :locals => { :commentable => @request, :visible => true }
-
end
-
-
1
def destroy
-
comment = Comment.find(params[:id])
-
unless comment.blank?
-
comment.destroy
-
end
-
@comments = @request.comments
-
render :partial => "list", :locals => { :commentable => @request, :visible => true }
-
end
-
-
1
private
-
1
def discover_request
-
@request = Request.find(params[:request_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class RequestsController < ApplicationController
-
-
1
before_filter :admin_login_required, :only => [ :describe, :undescribe, :destroy ]
-
1
before_filter :set_permitted_params, :only => [ :update ]
-
-
1
def set_permitted_params
-
@parameters = params[:request].reject{|k,v| !['request_metadata_attributes'].include?(k.to_s)}
-
end
-
1
attr_reader :parameters
-
# before_filter :find_request_from_id, :only => [ :filter_change_decision, :change_decision ]
-
-
1
def index
-
@no_filter_params, @study, @item, query_options = true, nil, nil, { :order => 'created_at DESC' }
-
-
# Ok, here we pick the initial source for the Requests. They either come from Request (as in all Requests), or they
-
# are limited by the Asset / Item.
-
request_source = Request
-
if params[:item_id]
-
@no_filter_params = false
-
@item = Item.find(params[:item_id])
-
request_source = @item.requests
-
elsif params[:asset_id]
-
@no_filter_params = false
-
@item = Asset.find(params[:asset_id])
-
request_source = @item.requests
-
end
-
-
# Now we can change the source for the Requests based on filtering parameters.
-
if params[:request_type_id]
-
@no_filter_params = false
-
@request_type = RequestType.find(params[:request_type_id])
-
request_source = request_source.request_type(params[:request_type_id])
-
elsif params[:request_type] and params[:workflow]
-
@no_filter_params = false
-
request_source = request_source.for_request_types(params[:request_type]).for_workflow(params[:workflow])
-
query_options[ :include ] = :user
-
end
-
if params[:study_id]
-
@no_filter_params = false
-
@study = Study.find(params[:study_id])
-
request_source = request_source.for_initial_study_id(params[:study_id])
-
end
-
if params[:state]
-
@no_filter_params = false
-
request_source = request_source.for_state(params[:state])
-
end
-
-
# Now, here we go: find all of the requests!
-
@requests =
-
if @no_filter_params
-
Request.paginate(:page => params[:page], :order => 'created_at DESC')
-
else
-
request_source.all(query_options)
-
end
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => Request.all.to_xml }
-
end
-
end
-
-
1
def edit
-
@request = Request.find(params[:id])
-
@request_types = RequestType.find_all_by_asset_type(@request.request_type.asset_type)
-
if current_user.is_administrator?
-
respond_to do |format|
-
format.html
-
end
-
else
-
flash[:error] = "You cannot update a request unless you're an administrator"
-
redirect_to request_path(@request)
-
end
-
end
-
-
1
def update
-
@request = Request.find(params[:id])
-
if redirect_if_not_owner_or_admin
-
return
-
end
-
-
unless params[:request][:request_type_id].nil?
-
unless @request.request_type_updatable?(params[:request][:request_type_id])
-
flash[:error] = "You can not change the request type."
-
redirect_to request_path(@request)
-
return
-
end
-
end
-
-
begin
-
if @request.update_attributes(parameters)
-
flash[:notice] = "Request details have been updated"
-
redirect_to request_path(@request)
-
else
-
flash[:error] = "Request was not updated. No change specified ?"
-
render :action => "edit", :id => @request.id
-
end
-
rescue => exception
-
error_message = "An error has occurred, category:'#{exception.class}'\ndescription:'#{exception.message}'"
-
EventFactory.request_update_note_to_manager(@request, current_user, error_message)
-
flash[:error] = "Failed to update request. " << error_message
-
render :action => "edit", :id => @request.id
-
end
-
end
-
-
1
def show
-
@request = Request.find(params[:id])
-
unless @request.user_id.blank?
-
@user = User.find(@request.user_id)
-
end
-
-
respond_to do |format|
-
format.html
-
format.xml
-
end
-
end
-
-
1
def additional
-
@request = Request.find(params[:id])
-
@additional = @request.request_type.create!(:initial_study => @request.study, :items => @request.items)
-
redirect_to request_path(@additional)
-
end
-
-
1
def reset
-
@request = Request.find(params[:id])
-
@request.reset!
-
flash[:notice] = "Request #{@request.id} was reset successfully"
-
if params[:study_id]
-
redirect_to study_requests_path(params[:study_id])
-
else
-
redirect_to requests_path
-
end
-
end
-
-
1
def cancel
-
@request = Request.find(params[:id])
-
if @request.cancelable?
-
if @request.cancel_before_started && @request.save
-
flash[:notice] = "Request #{@request.id} has been cancelled"
-
redirect_to request_path(@request)
-
else
-
flash[:error] = "Failed to cancel request #{@request.id}"
-
redirect_to request_path(@request)
-
end
-
else
-
flash[:error] = "Request #{@request.id} in progress. Can't be cancelled"
-
redirect_to request_path(@request)
-
end
-
end
-
-
# Displays history of events
-
1
def history
-
@request = Request.find(params[:id])
-
respond_to do |format|
-
format.html
-
format.xml { @request.events.to_xml }
-
format.json { @request.events.to_json }
-
end
-
end
-
-
1
def list_inboxes
-
@tasks = Task.all
-
end
-
-
1
def print
-
@request = Request.find(params[:id])
-
end
-
-
1
def print_items
-
@request = Request.find(params[:request_id])
-
printables = []
-
params[:printable].each do |key, value|
-
item = Item.find(key)
-
printables.push PrintBarcode::Label.new({ :number => key, :study => item.name, :suffix => "" })
-
end
-
if !printables.empty?
-
BarcodePrinter.print(printables, params[:printer])
-
end
-
flash[:notice] = "Your labels have been sent to printer #{params[:printer]}."
-
redirect_to request_path(@request)
-
rescue SOAP::FaultError
-
flash[:warning] = "There is a problem with the selected printer. Please report it to Systems."
-
redirect_to request_path(@request)
-
end
-
-
1
def expanded(options = {})
-
render :text => "", :status => :gone
-
end
-
-
1
def pending
-
render :text => "", :status => :gone
-
end
-
-
1
def incomplete_requests_for_family(options = {})
-
render :text => "", :status => :gone
-
end
-
-
1
def redirect_if_not_owner_or_admin
-
unless current_user == @request.user or current_user.is_administrator? or current_user.is_manager?
-
flash[:error] = "Request details can only be altered by the owner or a manager"
-
redirect_to request_path(@request)
-
return true
-
end
-
false
-
end
-
-
1
def copy
-
old_request = Request.find(params[:id])
-
new_request = old_request.copy
-
flash[:notice] = "Created request #{new_request.id}"
-
redirect_to asset_url(new_request.asset)
-
end
-
-
1
def reset_qc_information
-
@request = Request.find(params[:id])
-
@request.reset!
-
@event = Event.find(params[:event_id])
-
flash[:notice] = "QC event #{@event.id} has been deleted"
-
@event.destroy
-
redirect_to request_path(@request)
-
end
-
-
# Method used to migrate MX data from studies to pipelines
-
1
def mpx_requests_details
-
@requests = Request.migrate_mpx_requests
-
respond_to do |format|
-
format.json { render :json => @requests.to_json }
-
end
-
end
-
-
1
before_filter :find_request, :only => [ :filter_change_decision, :change_decision ]
-
-
1
def find_request
-
@request = Request.find(params[:id])
-
end
-
-
1
def filter_change_decision
-
@change_decision = Request::ChangeDecision.new(:request => @request, :user => @current_user)
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def change_decision
-
@change_decision = Request::ChangeDecision.new({:request => @request, :user => @current_user}.merge(params[:change_decision] || {})).execute!
-
flash[:notice] = "Update. Below you find the new situation."
-
redirect_to filter_change_decision_request_path(params[:id])
-
rescue Request::ChangeDecision::InvalidDecision => exception
-
flash[:error] = "Failed! Please, read the list of problem below."
-
@change_decision = exception.object
-
render(:action => :filter_change_decision)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class RobotVerificationsController < ApplicationController
-
1
before_filter :new_robot_verification
-
-
1
def index
-
end
-
-
1
def submission
-
barcode_hash = params[:barcodes]
-
-
errors = []
-
@robot_verification.validate_barcode_params(barcode_hash, &errors.method(:push))
-
if errors.empty?
-
get_fields_and_check(barcode_hash)
-
else
-
flash[:error] = errors
-
redirect_to :action => :index
-
end
-
end
-
-
1
def download
-
if @robot_verification.valid_submission?(params)
-
@robot_verification.set_plate_types(params[:source_plate_types])
-
@batch = Batch.find(params[:batch_id])
-
@destination_plate_id = Plate.with_machine_barcode(params[:destination_plate_barcodes].first.first).first.barcode
-
else
-
flash[:error] = "Error: #{@robot_verification.errors.join('; ')}"
-
redirect_to :action => :index
-
end
-
end
-
-
1
def get_fields_and_check(barcode_hash)
-
@batch = Batch.find_from_barcode(barcode_hash[:batch_barcode])
-
@user = User.find_by_barcode(Barcode.barcode_to_human!(barcode_hash[:user_barcode], User.prefix))
-
@all_labels = @robot_verification.expected_layout(@batch,barcode_hash[:destination_plate_barcode])
-
@robot = Robot.find_from_barcode(barcode_hash[:robot_barcode])
-
end
-
-
1
def new_robot_verification
-
@robot_verification = RobotVerification.new
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Roles::UsersController < ApplicationController
-
-
1
def index
-
@role = Role.find(params[:role_id], :include => [:users])
-
@users = User.all.select{|user| user.has_role? @role.name}
-
@users = @users.sort_by{|n| n.login}
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class RolesController < ApplicationController
-
-
1
def index
-
@roles = Role.all(:group => :name)
-
-
respond_to do |format|
-
format.html # index.html.erb
-
format.xml { render :xml => @roles }
-
end
-
end
-
-
1
def show
-
@role = Role.find(params[:id])
-
-
respond_to do |format|
-
format.html # show.html.erb
-
format.xml { render :xml => @role }
-
end
-
end
-
-
1
def new
-
@role = Role.new
-
-
respond_to do |format|
-
format.html # new.html.erb
-
format.xml { render :xml => @role }
-
end
-
end
-
-
1
def edit
-
@role = Role.find(params[:id])
-
end
-
-
1
def create
-
@role = Role.new(params[:role])
-
-
respond_to do |format|
-
if @role.save
-
flash[:notice] = 'Role was successfully created.'
-
format.html { redirect_to(@role) }
-
format.xml { render :xml => @role, :status => :created, :location => @role }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @role.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def update
-
@role = Role.find(params[:id])
-
-
respond_to do |format|
-
if @role.update_attributes(params[:role])
-
flash[:notice] = 'Role was successfully updated.'
-
format.html { redirect_to admin_role_path(@role) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @role.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@role = Role.find(params[:id])
-
@role.destroy
-
-
respond_to do |format|
-
format.html { redirect_to(admin_roles_path) }
-
format.xml { head :ok }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class SampleLogisticsController < ApplicationController
-
1
before_filter :slf_gel_login_required, :only => [:index, :qc_overview]
-
-
1
def qc_overview
-
@stock_plates = Plate.qc_started_plates.paginate :page => params[:page], :per_page => 500
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Samples::CommentsController < ApplicationController
-
1
before_filter :discover_sample
-
-
1
def index
-
@comments = @sample.comments.all(:order => "created_at ASC")
-
end
-
-
1
def create
-
@sample.comments.create(:description => params[:comment], :user_id => current_user.id)
-
@comments = @sample.comments
-
render :partial => "list", :locals => { :commentable => @sample, :visible => true }
-
end
-
-
1
def destroy
-
comment = Comment.find(params[:id])
-
unless comment.blank?
-
comment.destroy
-
end
-
@comments = @sample.comments
-
render :partial => "list", :locals => { :commentable => @sample, :visible => true }
-
end
-
-
1
private
-
1
def discover_sample
-
@sample = Sample.find(params[:sample_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Samples::StudiesController < ApplicationController
-
-
1
def index
-
studies = Sample.find(params[:sample_id]).studies
-
respond_to do |format|
-
format.xml { render :xml => studies.to_xml }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
1
class SamplesController < ApplicationController
-
1
include XmlCacheHelper::ControllerHelper
-
-
#require 'curb'
-
-
1
before_filter :admin_login_required, :only => [ :administer, :destroy ]
-
-
1
def index
-
@samples = Sample.order('created_at DESC').page(params[:page])
-
respond_to do |format|
-
format.html
-
format.xml
-
format.json { render :json => Sample.all.to_json }
-
end
-
end
-
-
1
def new
-
@sample = Sample.new
-
@workflows = Submission::Workflow.all
-
@studies = Study.all.sort_by {|p| p.name }
-
end
-
-
1
def create
-
@sample = Sample.new(params[:sample])
-
-
study_id = params[:study_id]
-
if study_id
-
study = Study.find(study_id)
-
study.samples << @sample
-
end
-
-
respond_to do |format|
-
if @sample.save
-
flash[:notice] = "Sample successfully created"
-
format.html { redirect_to sample_path(@sample) }
-
format.xml { render :xml => @sample, :status => :created, :location => @sample }
-
format.json { render :json => @sample, :status => :created, :location => @sample }
-
else
-
@workflows = Submission::Workflow.all
-
flash[:error] = "Problems creating your new sample"
-
format.html { render :action => :new }
-
format.xml { render :xml => @sample.errors, :status => :unprocessable_entity }
-
format.json { render :json => @sample.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def show
-
@sample = Sample.find(params[:id], :include => :assets)
-
@studies = Study.all(:conditions => ["state = ? OR state = ?", "pending", "active"], :order => :name)
-
-
respond_to do |format|
-
format.html
-
format.xml { cache_xml_response(@sample) }
-
format.json { render :json => @sample.to_json }
-
end
-
end
-
-
1
def release
-
@sample = Sample.find(params[:id])
-
redirect_if_not_owner_or_admin_otherwise do
-
unless @sample.released?
-
@sample.release
-
flash[:notice] = "Sample '#{@sample.name}' publically released"
-
else
-
flash[:notice] = "Sample '#{@sample.name}' already publically released"
-
end
-
redirect_to sample_path(@sample)
-
end
-
end
-
-
-
1
def edit
-
@sample = Sample.find(params[:id])
-
redirect_if_not_owner_or_admin_otherwise do
-
if @sample.released? && ! current_user.is_administrator?
-
flash[:error] = "Cannot edit publically released sample"
-
redirect_to sample_path(@sample)
-
return
-
end
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @samples.to_xml }
-
format.json { render :json => @samples.to_json }
-
end
-
end
-
end
-
-
-
1
def update
-
@sample = Sample.find(params[:id])
-
redirect_if_not_owner_or_admin_otherwise do
-
begin
-
cleaned_params = clean_params_from_check(params[:sample])
-
@sample.update_attributes!(cleaned_params)
-
flash[:notice] = "Sample details have been updated"
-
redirect_to sample_path(@sample)
-
rescue ActiveRecord::RecordInvalid => exception
-
@workflows = Submission::Workflow.all
-
flash[:error] = "Failed to update attributes for sample"
-
render :action => "edit", :id => @sample.id
-
end
-
end
-
end
-
-
1
def history
-
@sample = Sample.find(params[:id])
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def add_to_study
-
sample = Sample.find(params[:id])
-
study = Study.find(params[:study][:id])
-
study.samples << sample
-
redirect_to sample_path(sample)
-
rescue ActiveRecord::RecordInvalid => exception
-
flash[:error] = exception.record.errors.full_messages
-
redirect_to sample_path(sample)
-
end
-
-
1
def remove_from_study
-
study = Study.find(params[:study_id])
-
sample = Sample.find(params[:id])
-
StudySample.find(:first, :conditions=>{:study_id=>params[:study_id],:sample_id=>params[:id]}).destroy
-
flash[:notice] = "Sample was removed from study #{study.name.humanize}"
-
redirect_to sample_path(sample)
-
end
-
-
1
def show_accession
-
@sample = Sample.find(params[:id])
-
respond_to do |format|
-
xml_text =@sample.accession_service.accession_sample_xml(@sample)
-
format.xml { render(:text => xml_text) }
-
end
-
end
-
-
1
def accession
-
@sample = Sample.find(params[:id])
-
@sample.validate_ena_required_fields!
-
@sample.accession_service.submit_sample_for_user(@sample, current_user)
-
-
flash[:notice] = "Accession number generated: #{ @sample.sample_metadata.sample_ebi_accession_number }"
-
redirect_to(sample_path(@sample))
-
rescue ActiveRecord::RecordInvalid => exception
-
flash[:error] = "Please fill in the required fields: #{@sample.errors.full_messages.join(', ')}"
-
redirect_to(edit_sample_path(@sample))
-
rescue AccessionService::NumberNotRequired => exception
-
flash[:warning] = exception.message || 'An accession number is not required for this study'
-
redirect_to(sample_path(@sample))
-
rescue AccessionService::NumberNotGenerated => exception
-
flash[:warning] = 'No accession number was generated'
-
redirect_to(sample_path(@sample))
-
rescue AccessionService::AccessionServiceError => exception
-
flash[:error] = exception.message
-
redirect_to(sample_path(@sample))
-
end
-
-
1
def taxon_lookup
-
if params[:term]
-
url= configatron.taxon_lookup_url+"/esearch.fcgi?db=taxonomy&term=#{params[:term].gsub(/\s/, '_')}"
-
elsif params[:id]
-
url = configatron.taxon_lookup_url+"/efetch.fcgi?db=taxonomy&mode=xml&id=#{params[:id]}"
-
else return
-
end
-
-
rc = RestClient::Resource.new(URI.parse(url).to_s)
-
if configatron.disable_web_proxy == true
-
RestClient.proxy = ''
-
elsif not configatron.proxy.blank?
-
RestClient.proxy= configatron.proxy
-
rc.headers["User-Agent"] = "Internet Explorer 5.0"
-
end
-
#rc.verbose = true
-
body = rc.get.body
-
-
respond_to do |format|
-
format.js {render :text =>body}
-
format.xml {render :text =>body}
-
# format.html {render :nothing}
-
end
-
end
-
-
1
private
-
-
1
def redirect_if_not_owner_or_admin_otherwise(&block)
-
return yield if current_user.owner?(@sample) or current_user.is_administrator? or current_user.is_manager?
-
flash[:error] = "Sample details can only be altered by the owner or an administrator or manager"
-
redirect_to sample_path(@sample)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# This is the base of the Sample registration sub-application
-
1
class Sdb::BaseController < ApplicationController
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Sdb::HomeController < Sdb::BaseController
-
1
def index
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
class Sdb::SampleManifestsController < Sdb::BaseController
-
1
before_filter :set_sample_manifest_id, :only => [:show, :generated]
-
1
before_filter :validate_type, :only => [:new, :create]
-
-
# Upload the manifest and store it for later processing
-
1
def upload
-
if (params[:sample_manifest].blank?) || (params[:sample_manifest] && params[:sample_manifest][:uploaded].blank? )
-
flash[:error] = "No CSV file uploaded"
-
return
-
end
-
-
@sample_manifest = SampleManifest.find_sample_manifest_from_uploaded_spreadsheet(params[:sample_manifest][:uploaded])
-
if @sample_manifest.nil?
-
flash[:error] = "Cannot find details about the sample manifest"
-
return
-
end
-
@sample_manifest.update_attributes(params[:sample_manifest])
-
@sample_manifest.process(current_user, params[:sample_manifest][:override] == "1")
-
flash[:notice] = "Manifest being processed"
-
rescue CSV::MalformedCSVError
-
flash[:error] = "Invalid CSV file"
-
ensure
-
redirect_to sample_manifests_path
-
end
-
-
1
def export
-
@manifest = SampleManifest.find(params[:id])
-
send_data(@manifest.generated_document.current_data,
-
:filename => "manifest_#{@manifest.id}.xls",
-
:type => 'application/excel')
-
end
-
-
1
def uploaded_spreadsheet
-
@manifest = SampleManifest.find(params[:id])
-
send_data(@manifest.uploaded_document.current_data,
-
:filename => "manifest_#{@manifest.id}.csv",
-
:type => 'application/excel')
-
end
-
-
1
def new
-
@sample_manifest = SampleManifest.new(:asset_type => params[:type])
-
@studies = Study.all.sort{ |a,b,| a.name <=> b.name }
-
@suppliers = Supplier.all.sort{ |a,b,| a.name <=> b.name }
-
@barcode_printers = @sample_manifest.applicable_barcode_printers
-
@templates = @sample_manifest.applicable_templates
-
end
-
-
1
def printer_options(params)
-
barcode_printer_id = params[:sample_manifest][:barcode_printer]
-
barcode_printer = BarcodePrinter.find(barcode_printer_id) unless barcode_printer_id.blank?
-
return { :barcode_printer => barcode_printer,
-
:only_first_label => (params[:sample_manifest][:only_first_label].to_i == 1) }
-
end
-
-
1
def template_manifest_options(params)
-
params[:sample_manifest].merge(:user => current_user, :rapid_generation => true).except!(:only_first_label, :barcode_printer)
-
end
-
-
1
def create
-
template = SampleManifestTemplate.find(params[:sample_manifest].delete(:template))
-
-
ActiveRecord::Base.transaction do
-
@sample_manifest = template.create!(template_manifest_options(params))
-
-
@sample_manifest.generate
-
template.generate(@sample_manifest)
-
end
-
printer_options = printer_options(params)
-
barcode_printer=printer_options[:barcode_printer]
-
-
unless barcode_printer.nil?
-
@sample_manifest.print_labels(barcode_printer, printer_options)
-
end
-
-
if !@sample_manifest.manifest_errors.empty?
-
flash[:error] = @sample_manifest.manifest_errors.join(", ")
-
@sample_manifest.destroy
-
redirect_to new_sample_manifest_path
-
else
-
redirect_to sample_manifest_path(@sample_manifest)
-
end
-
end
-
-
# Show the manifest
-
1
def show
-
1
@samples = @sample_manifest.samples.paginate(:page => params[:page])
-
end
-
-
1
def index
-
pending_sample_manifests = SampleManifest.pending_manifests.paginate(:page => params[:page])
-
completed_sample_manifests = SampleManifest.completed_manifests.paginate(:page => params[:page])
-
@display_manifests = pending_sample_manifests | completed_sample_manifests
-
@sample_manifests = SampleManifest.paginate(:page => params[:page])
-
end
-
-
1
private
-
1
def set_sample_manifest_id
-
1
@sample_manifest = SampleManifest.find(params[:id])
-
end
-
-
1
def validate_type
-
return true if SampleManifest.supported_asset_type?(params[:type])
-
flash[:error] = "'#{params[:type]}' is not a supported manifest type."
-
begin
-
redirect_to :back
-
rescue ActionController::RedirectBackError
-
redirect_to sample_manifests_path
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Sdb::SuppliersController < Sdb::BaseController
-
-
# Show all suppliers
-
1
def index
-
@suppliers = Supplier.all
-
end
-
-
# Create a supplier
-
1
def new
-
@supplier = Supplier.new
-
end
-
-
# Create a supplier
-
1
def create
-
@supplier = Supplier.new(params[:supplier])
-
-
respond_to do |format|
-
if @supplier.save
-
flash[:notice] = 'Supplier was successfully created.'
-
format.html { redirect_to( '/sdb/') }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
1
def edit
-
@supplier = Supplier.find(params[:id])
-
end
-
-
# Update a supplier
-
1
def update
-
@supplier = Supplier.find(params[:id])
-
-
respond_to do |format|
-
if @supplier.update_attributes(params[:supplier])
-
flash[:notice] = 'Supplier was successfully updated'
-
format.html { redirect_to(@supplier) }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
# Show a supplier
-
1
def show
-
@supplier = Supplier.find(params[:id])
-
end
-
-
1
def sample_manifests
-
@supplier = Supplier.find(params[:id])
-
@sample_manifests = @supplier.sample_manifests.paginate(:page => params[:page])
-
end
-
-
1
def studies
-
@supplier = Supplier.find(params[:id])
-
@studies = @supplier.studies.paginate(:page => params[:page])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
class SearchesController < ApplicationController
-
1
include Informatics::Globals
-
1
include SearchBehaviour
-
1
alias_method(:index, :search)
-
-
1
private
-
-
# SEARCHABLE_CLASSES = [ Project, Study, Sample, Asset, AssetGroup, Request, Supplier ]
-
1
def searchable_classes
-
params[:type].blank? ? global_searchable_classes : [global_searchable_classes.detect {|klass| klass.name == params[:type] }]
-
end
-
-
-
1
def extended
-
false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class SequenomController < ApplicationController
-
1
EmptyBarcode = Class.new(StandardError)
-
-
# An instance of this class does the work of updating a Plate with the step performed.
-
1
class SequenomStep
-
1
attr_reader :name
-
-
1
def initialize(name)
-
4
@name = name
-
end
-
-
1
def update_plate(plate, user)
-
plate.events.create!(:message => I18n.t('sequenom.events.message', :step => self.name), :created_by => user.login)
-
yield(self)
-
end
-
end
-
-
# Here are all of the steps that can be performed
-
5
STEPS = [ 'PCR Mix', 'SAP Mix', 'IPLEX Mix', 'HPLC Water' ].map { |name| SequenomStep.new(name) }
-
1
class << STEPS
-
1
def for(step_name)
-
self.find { |step| step.name == step_name } or raise "Cannot find the Sequenom step '#{ step_name }'"
-
end
-
end
-
-
1
before_filter :login_required
-
1
before_filter :find_plate_from_id, :only => [ :show, :update ]
-
-
1
def index
-
# Do nothing, fall through to the view
-
end
-
-
1
def search
-
redirect_to sequenom_plate_path(@plate)
-
end
-
-
1
def show
-
# Do nothing as @plate is setup by the before filter, fall through to the view
-
end
-
-
1
def update
-
ActiveRecord::Base.transaction do
-
STEPS.for(params[:sequenom_step]).update_plate(@plate, @user) do |step|
-
flash[:notice] = I18n.t(
-
'sequenom.notices.step_completed',
-
:step => step.name, :barcode => @plate.ean13_barcode, :human_barcode => @plate.sanger_human_barcode
-
)
-
end
-
end
-
redirect_to sequenom_plate_path(@plate)
-
end
-
-
# Although this might seem stupid this actually enables us to use different filters
-
# around this action from the #update action. We simply need to ensure that @user
-
# and @plate are setup before we get to the action code.
-
1
alias_method(:quick_update, :update)
-
-
1
private
-
-
1
def find_plate_from_id
-
@plate = Plate.find(params[:id])
-
rescue ActiveRecord::RecordNotFound => exception
-
flash[:error] = I18n.t('sequenom.errors.plate.not_found_by_id')
-
redirect_to sequenom_root_path
-
end
-
-
# Defines a filter method that will lookup an instance of the specified model class, assigning
-
# an instance variable or redirecting to the sequenom_root_path on error. The block passed
-
# should take two parameters (the barcode and the human version of that barcode) and return the
-
# value that can be used by +model_class.find_by_barcode+. +filter_options+ are exactly as
-
# would be specified for a +before_filter+.
-
1
def self.find_by_barcode_filter(model_class, filter_options, &block)
-
2
name = model_class.name.underscore
-
2
filter_name = :"find_#{ name }_from_barcode"
-
2
rescue_exception_for_filter = :"rescue_#{ filter_name }"
-
-
2
define_method(filter_name) do
-
begin
-
barcode = params[:"#{ name }_barcode"]
-
raise EmptyBarcode, "The #{ name } barcode appears to be empty" if barcode.blank?
-
human_barcode = Barcode.barcode_to_human!(barcode, model_class.prefix)
-
object = model_class.find_by_barcode(block.call(barcode, human_barcode))
-
raise ActiveRecord::RecordNotFound, "Could not find a #{ name } with barcode #{ barcode }" if object.nil?
-
instance_variable_set("@#{ name }", object)
-
rescue StandardError => exception
-
send(rescue_exception_for_filter, exception, barcode, human_barcode)
-
end
-
end
-
2
define_method(rescue_exception_for_filter) do |exception,barcode,human_barcode|
-
case
-
when ActiveRecord::RecordNotFound === exception
-
flash[:error] = I18n.t("sequenom.errors.#{ name }.not_found_by_barcode", :barcode => barcode, :human_barcode => human_barcode)
-
redirect_to sequenom_root_path
-
-
when Barcode::InvalidBarcode === exception
-
flash[:error] = I18n.t("sequenom.errors.#{ name }.invalid_barcode", :barcode => barcode, :human_barcode => human_barcode)
-
redirect_to sequenom_root_path
-
-
when EmptyBarcode === exception
-
flash[:error] = I18n.t("sequenom.errors.#{ name }.empty_barcode")
-
redirect_to sequenom_root_path
-
-
else
-
flash[:error] = I18n.t("sequenom.errors.#{ name }.unknown")
-
redirect_to sequenom_root_path
-
end
-
end
-
-
2
before_filter(filter_name, filter_options)
-
end
-
-
1
find_by_barcode_filter(User, :only => [ :update, :quick_update ]) { |barcode,human_barcode| human_barcode }
-
1
find_by_barcode_filter(Plate, :only => [ :search, :quick_update ]) { |barcode,human_barcode| Barcode.number_to_human(barcode) }
-
-
# Handle the case where ActiveRecord::RecordNotFound is raised when looking for a Plate by
-
# physically creating the Plate in the database!
-
1
def rescue_find_plate_from_barcode_with_create(exception, barcode, human_barcode)
-
rescue_find_plate_from_barcode_without_create(exception, barcode, human_barcode) unless ActiveRecord::RecordNotFound === exception
-
@plate = Plate.create!(:barcode => Barcode.number_to_human(barcode))
-
end
-
1
alias_method_chain(:rescue_find_plate_from_barcode, :create)
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class SequenomQcPlatesController < ApplicationController
-
1
def new
-
@barcode_printers = BarcodePrinterType.find_by_name("384 Well Plate").barcode_printers
-
@barcode_printers = BarcodePrinter.find(:all, :order => "name asc") if @barcode_printers.blank?
-
@input_plate_names = input_plate_names()
-
end
-
-
1
def create
-
@input_plate_names = input_plate_names()
-
@barcode_printers = BarcodePrinter.all
-
barcode_printer = BarcodePrinter.find(params[:barcode_printer][:id])
-
number_of_barcodes = params[:number_of_barcodes].to_i
-
input_plate_names = params[:input_plate_names]
-
user_barcode = params[:user_barcode]
-
-
# It's been decided that a blank dummy plate will be created for each barcode label required
-
# Any information stored against the plate's wells should be passed through to the stock plate
-
# so should be findable.
-
new_plates = []
-
-
# This will hold the first bad plate with errors preventing it's creation
-
bad_plate = nil
-
-
ActiveRecord::Base.transaction do
-
(1..number_of_barcodes).each do
-
sequenom_qc_plate = SequenomQcPlate.new(
-
:plate_prefix => params[:plate_prefix],
-
:gender_check_bypass => gender_check_bypass,
-
:user_barcode => user_barcode
-
)
-
#TODO: create a factory object
-
-
# Need to be done before saving the plate
-
valid = input_plate_names && sequenom_qc_plate.compute_and_set_name(input_plate_names)
-
errors = sequenom_qc_plate.errors.inject({}) { |h, (k, v)| h.update(k=>v) }
-
if sequenom_qc_plate.save and valid and sequenom_qc_plate.add_event_to_stock_plates(user_barcode)
-
new_plates << sequenom_qc_plate
-
else
-
# If saving any of our new plates fails then catch that plate, for errors
-
# and move straight on to sending a response
-
bad_plate = sequenom_qc_plate
-
errors.each do |att, value|
-
bad_plate.errors.add(att, value)
-
end
-
break
-
end
-
end
-
end
-
-
respond_to do |format|
-
if bad_plate
-
# Something's gone wrong, render the errors on the first plate that failed
-
flash[:error] = bad_plate.errors.full_messages || "Failed to create Sequenom QC Plate"
-
format.html { render :new }
-
else
-
# Everything's tickity boo so...
-
# print the a label for each plate we created
-
new_plates.each { |p| p.print_labels(barcode_printer) }
-
-
# and redirect to a fresh page with an appropriate flash[:notice]
-
first_plate = new_plates.first
-
flash[:notice] = "Sequenom #{first_plate.plate_prefix} Plate #{first_plate.name} successfully created and labels printed."
-
-
format.html { redirect_to new_sequenom_qc_plate_path }
-
end
-
end
-
-
end
-
-
1
def index
-
@sequenom_qc_plates = SequenomQcPlate.paginate(:page => params[:page], :order => "created_at desc")
-
end
-
-
1
private
-
# If the current user isn't allowed to bypass the geneder checks don't let them
-
# even they're sneaky enough to try and send back the param value anyway!
-
1
def gender_check_bypass
-
if current_user.slf_manager? || current_user.manager_or_administrator?
-
params[:gender_check_bypass]
-
else
-
false
-
end
-
end
-
-
1
def input_plate_names
-
input_plate_names = {}
-
(1..4).each { |i| input_plate_names[i] = params[:input_plate_names].try(:[],i.to_s) || "" }
-
input_plate_names
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
class SessionsController < ApplicationController
-
1
include Informatics::Globals
-
-
1
skip_before_filter :login_required
-
-
1
def index
-
redirect_to :action => :login
-
end
-
-
1
def settings
-
end
-
-
1
def login
-
return unless request.post?
-
self.current_user = User.authenticate(params[:login], params[:password])
-
if logged_in?
-
flash[:notice] = "Logged in successfully"
-
redirect_back_or_default(:controller => :studies)
-
else
-
if params
-
flash[:notice] = "Your log in details don't match our records. Please try again."
-
end
-
end
-
end
-
-
1
def logout
-
self.current_user.forget_me if logged_in?
-
cookies.delete :auth_token
-
reset_session
-
flash[:notice] = "You have been logged out."
-
redirect_back_or_default(:controller => :studies)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Studies::AssetGroupsController < ApplicationController
-
1
include BarcodePrintersController::Print
-
1
def index
-
@study = Study.find(params[:study_id])
-
@asset_groups = @study.asset_groups
-
-
respond_to do |format|
-
format.html # index.html.erb
-
format.xml { render :xml => @asset_groups }
-
end
-
end
-
-
1
def show
-
@asset_group = AssetGroup.find(params[:id])
-
@study = Study.find(params[:study_id])
-
-
respond_to do |format|
-
format.html # show.html.erb
-
format.xml { render :xml => @asset_group }
-
end
-
end
-
-
1
def new
-
@asset_group = AssetGroup.new
-
@study = Study.find(params[:study_id])
-
-
respond_to do |format|
-
format.html # new.html.erb
-
format.xml { render :xml => @asset_group }
-
end
-
end
-
-
1
def edit
-
@asset_group = AssetGroup.find(params[:id])
-
@study = Study.find(params[:study_id])
-
end
-
-
1
def create
-
@study = Study.find(params[:study_id])
-
@asset_group = AssetGroup.new(params[:asset_group])
-
@asset_group.study = @study
-
-
respond_to do |format|
-
if @asset_group.save
-
flash[:notice] = 'AssetGroup was successfully created.'
-
format.html { redirect_to study_asset_group_path(@study, @asset_group) }
-
format.xml { render :xml => @asset_group, :status => :created, :location => @asset_group }
-
format.json { render :json => @asset_group, :status => :created, :location => @asset_group }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @asset_group.errors, :status => :unprocessable_entity }
-
format.json { render :json => @asset_group.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def update
-
@asset_group = AssetGroup.find(params[:id])
-
@study = Study.find(params[:study_id])
-
-
respond_to do |format|
-
if @asset_group.update_attributes(params[:asset_group])
-
flash[:notice] = 'AssetGroup was successfully updated.'
-
format.html { redirect_to study_asset_group_path(@study, @asset_group) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @asset_group.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@asset_group = AssetGroup.find(params[:id])
-
@asset_group.destroy
-
@study = Study.find(params[:study_id])
-
-
respond_to do |format|
-
format.html { redirect_to(study_asset_groups_url(@study)) }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def search
-
@study = Study.find(params[:study_id])
-
query = params[:q]
-
if query.blank? or query.length < 2
-
#We should not blame the user, we should instead help.
-
# - By returning the X most recent ones together with an explanation.
-
flash[:error] = "Search too wide. Please make your query more specific."
-
redirect_to study_asset_group_path(@study)
-
return
-
else
-
@assets = Asset.find(:all, :conditions => ['name like ?', "%#{query}%"] )
-
end
-
@asset_group = AssetGroup.find(params[:id])
-
respond_to do |format|
-
format.html # index.html.erb
-
format.xml { render :xml => @assets }
-
end
-
end
-
-
1
def add
-
@asset_group = AssetGroup.find(params[:id])
-
@study = Study.find(params[:study_id])
-
if params[:asset]
-
ids = params[:asset].map {|a| a[1] == "1" ? a[0] : nil }.select {|a| !a.nil? }
-
@assets = Asset.find(ids)
-
@asset_group.assets << @assets
-
end
-
-
respond_to do |format|
-
format.html { redirect_to(study_asset_group_url(@study,@asset_group)) }
-
format.xml { render :xml => @assets }
-
format.json { render :json => @assets }
-
end
-
end
-
-
1
def printing
-
@study = Study.find(params[:study_id])
-
@asset_groups = @study.asset_groups
-
end
-
-
1
def print
-
@asset_group = AssetGroup.find(params[:id])
-
@study = Study.find(params[:study_id])
-
-
@assets = @asset_group ? @asset_group.assets.select {|asset| asset.is_a?(Barcode::Barcodeable)} : []
-
-
unbarcoded = @asset_group.assets.reject {|asset| asset.is_a?(Barcode::Barcodeable)}
-
@unbarcoded_types = unbarcoded.map {|ub| ub.sti_type.pluralize.humanize }.uniq.to_sentence
-
@unbarcoded_count = unbarcoded.length
-
@containers = unbarcoded.map {|ub| ub.container }.uniq.select {|container| container.is_a?(Barcode::Barcodeable) }
-
end
-
-
1
def print_labels
-
@asset_group = AssetGroup.find(params[:id])
-
@study = Study.find(params[:study_id])
-
print_asset_labels(study_asset_groups_path(@study), print_study_asset_group_path(@study, @asset_group))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Studies::CommentsController < ApplicationController
-
1
before_filter :discover_study
-
-
1
def index
-
@comments = @study.comments.all(:order => "created_at ASC")
-
end
-
-
1
def create
-
@study.comments.create(:description => params[:comment], :user_id => current_user.id)
-
@comments = @study.comments
-
render :partial => "list", :locals => { :commentable => @study, :visible => true }
-
end
-
-
1
def destroy
-
comment = Comment.find(params[:id])
-
unless comment.blank?
-
comment.destroy
-
end
-
@comments = @study.comments
-
render :partial => "list", :locals => { :commentable => @study, :visible => true }
-
end
-
-
1
private
-
1
def discover_study
-
@study = Study.find(params[:study_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Studies::DocumentsController < ApplicationController
-
1
before_filter :get_study_id
-
-
1
def index
-
@study = Study.find(params[:study_id])
-
@documents = @study.documents
-
end
-
-
-
1
def new
-
@study = Study.find(params[:study_id])
-
end
-
-
1
def create
-
document_settings = params[:document]
-
document_settings[:documentable] = @study
-
@document = Document.new(document_settings)
-
begin
-
if @document.save
-
flash[:notice] = "Document was saved okay"
-
redirect_to [:admin, @study], :status => 303
-
else
-
render :action => "new"
-
end
-
rescue ActiveRecord::StatementInvalid
-
flash[:error] = "Something bad happened. Perhaps karma has caught up with you?"
-
redirect_to [:admin, @study], :status => 303
-
end
-
end
-
-
1
def show
-
@document = Document.find(params[:id])
-
send_data @document.current_data, :filename => @document.filename, :type => @document.content_type, :disposition => 'inline'
-
end
-
-
1
def destroy
-
@document = Document.find(params[:id])
-
if @document.destroy
-
flash[:notice] = "Document was successfully deleted"
-
redirect_to [:admin, @study], :status => 303
-
else
-
flash[:error] = "Document cannot be destroyed"
-
redirect_to [:admin, @study], :status => 303
-
end
-
end
-
-
1
private
-
-
1
def get_study_id
-
@study = Study.find(params[:study_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Studies::EventsController < ApplicationController
-
-
1
def index
-
@study = Study.find(params[:study_id])
-
@events = @study.events.all(:order => "created_at ASC")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Studies::SampleRegistrationController < ApplicationController
-
1
before_filter :load_study
-
-
1
def index
-
end
-
-
1
def create
-
# We have to remap the contents of the 'sample_registrars' parameter from a hash to an array, because
-
# that's what it actually is: a map from index to attributes for that SampleRegistrar instance.
-
attributes = clean_params_from_check(params['sample_registrars']).inject([]) do |attributes,(index_as_string,parameters)|
-
attributes[index_as_string.to_i] = parameters.merge(:study => @study, :user => current_user)
-
attributes
-
end.compact
-
-
@sample_registrars = SampleRegistrar.register!(attributes)
-
flash[:notice] = 'Your samples have been registered'
-
respond_to do |format|
-
format.html { redirect_to study_path(@study) }
-
format.json { render(:json => flash.to_json) }
-
format.xml { render(:xml => flash.to_xml) }
-
end
-
rescue SampleRegistrar::NoSamplesError => exception
-
flash.now[:error] = 'You do not appear to have specified any samples'
-
@sample_registrars = [ SampleRegistrar.new ]
-
render(:action => 'new')
-
rescue SampleRegistrar::RegistrationError => exception
-
flash.now[:error] = 'Your samples have not been registered'
-
@sample_registrars = exception.sample_registrars
-
render(:action => 'new')
-
end
-
-
1
def new
-
@sample_registrars = [ SampleRegistrar.new ]
-
end
-
-
1
def spreadsheet
-
flash.now[:notice] = "Processing your file: please wait a few minutes..."
-
@sample_registrars = SampleRegistrar.from_spreadsheet(params['file'], @study, current_user)
-
flash.now[:notice] = 'Your file has been processed'
-
render :new
-
rescue SampleRegistrar::SpreadsheetError => exception
-
flash[:notice] = 'Your file has been processed'
-
flash[:error] = exception.message
-
redirect_to upload_study_sample_registration_index_path
-
end
-
-
1
def upload
-
@workflow = @current_user.workflow if ! @current_user.nil? && ! @current_user.workflow.nil?
-
end
-
-
1
private
-
-
1
def load_study
-
@study = Study.find(params[:study_id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Studies::SamplesController < ApplicationController
-
-
1
def index
-
@study = Study.find(params[:study_id])
-
@samples = @study.samples.all(:order => "created_at ASC")
-
-
respond_to do |format|
-
format.html
-
format.json { render :json => @samples.to_json }
-
format.xml { render :xml => @samples.to_xml }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
1
class Studies::WorkflowsController < ApplicationController
-
1
before_filter :discover_study, :discover_workflow
-
-
1
before_filter :setup_tabs, :only => [ :show, :show_summary ]
-
-
1
def setup_tabs
-
@total_requests = compute_total_request(@study)
-
@cache = { :total => @total_requests }
-
-
@request_types = @workflow.request_types.all(:order => "`order` ASC").reject { |r| @total_requests[r].zero? }
-
-
@basic_tabs = ["Summary", "Sample progress", "Assets progress"]
-
@summaries = @basic_tabs + @request_types.map(&:name)
-
end
-
1
private :setup_tabs
-
-
1
def show
-
unless @current_user.nil?
-
@current_user.workflow = @workflow
-
@current_user.save!
-
end
-
@workflows = Submission::Workflow.find(:all, :order => ["name DESC"])
-
-
@default_tab_label = "Sample progress"
-
@summary = params[:summary].to_i
-
@summary = @basic_tabs.index(@default_tab_label) if params[:summary].nil?
-
-
@submissions = @study.submissions_for_workflow(@workflow)
-
-
# We need to propagate the extra_parameters - as page - to the summary partial
-
@extra_params = params.dup
-
[:summary, :study_id, :id, :action, :controller].each do |key|
-
@extra_params.delete key
-
end
-
-
respond_to do |format|
-
format.html
-
format.xml
-
format.json { render :json => Study.all.to_json }
-
end
-
-
end
-
-
1
def show_summary
-
# Dirty : in ajax request, paramter are escaped twice ...
-
params.each do |key, value|
-
new_key = key.sub(/^amp;/, "")
-
next if new_key == key
-
params[new_key]=value
-
end
-
page_params= { :page => params[:page] || 1, :per_page => params[:per_page] || 50 }
-
-
if request.xhr?
-
@default_tab_label = "Assets progress"
-
@summary = params[:summary].to_i
-
@summary = @basic_tabs.index(@default_tab_label) if params[:summary].nil?
-
-
case @summaries[@summary]
-
when "Sample progress"
-
@page_elements = @study.samples.paginate(page_params)
-
sample_ids = @page_elements.map(&:id)
-
render :partial => "sample_progress"
-
when "Assets progress"
-
@asset_type = Aliquot::Receptacle.descendants.detect {|cls| cls.name == params[:asset_type] } || Aliquot::Receptacle
-
@asset_type_name = params.fetch(:asset_type,'All Assets').underscore.humanize
-
@page_elements= @study.assets_through_aliquots.of_type(@asset_type).paginate(page_params)
-
asset_ids = @page_elements.map { |e| e.id }
-
-
@cache.merge!(:passed => @passed_asset_request, :failed => @failed_asset_request)
-
render :partial => "asset_progress"
-
when "Summary"
-
@page_elements= @study.assets_through_requests.for_summary.paginate(page_params)
-
asset_ids = @page_elements.map { |e| e.id }
-
render :partial => "summary"
-
else
-
@request_type = @request_types[@summary - @basic_tabs.size]
-
@assets_to_detail = @study.requests.request_type(@request_type).with_asset.all(:include =>:asset).map(&:asset).uniq
-
-
unless @assets_to_detail.empty?
-
render :partial => "summary_for_request_type"
-
else
-
render :text => "No requests of this type can be found"
-
end
-
end
-
else
-
page_params[:summary]= params[:summary]
-
redirect_to study_workflow_path(@study, @workflow, page_params)
-
end
-
end
-
-
1
def summary
-
s = UiHelper::Summary.new
-
@summary = s.load(@study, @workflow).paginate :page => params[:page], :order => 'created_at DESC'
-
# @summary.load(@study, @workflow)
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def compute_total_request(study)
-
total_requests = { }
-
report = @study.total_requests_report
-
@workflow.request_types.each do |rt|
-
total_requests[rt] = report[rt.id]||0
-
end
-
total_requests
-
end
-
-
1
def group_count(enumerable)
-
map = Hash.new { |hash, key| hash[key]= Hash.new 0 } # defining default value for nested hash
-
enumerable.each do |e|
-
groups = yield(e)
-
groups.each do |g_id, count|
-
map[g_id.to_i][e]= count
-
end
-
end
-
map
-
end
-
-
1
private
-
1
def discover_study
-
@study = Study.find(params[:study_id])
-
flash.now[:warning] = @study.warnings if @study.warnings.present?
-
end
-
-
1
def discover_workflow
-
@workflow = Submission::Workflow.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2015 Genome Research Ltd.
-
1
require "rexml/document"
-
-
1
class StudiesController < ApplicationController
-
1
include REXML
-
1
include Informatics::Globals
-
1
include XmlCacheHelper::ControllerHelper
-
-
1
before_filter :login_required
-
1
before_filter :admin_login_required, :only => [:new_plate_submission, :create_plate_submission, :settings, :administer, :manage, :managed_update, :grant_role, :remove_role]
-
1
before_filter :manager_login_required, :only => [ :close, :open, :related_studies, :relate_study, :unrelate_study]
-
-
1
around_filter :rescue_validation, :only => [:close, :open]
-
-
1
def setup_studies_from_scope(exclude_nested_resource = false)
-
if logged_in? and not exclude_nested_resource
-
@alternatives = [
-
"interesting", "followed", "managed & active", "managed & inactive",
-
"pending", "pending ethical approval", "contaminated with human dna",
-
"remove x and autosomes", "active", "inactive", "collaborations", "all"
-
]
-
@studies = studies_from_scope(@alternatives[params[:scope].to_i])
-
elsif params[:project_id] && !(project = Project.find(params[:project_id])).nil?
-
@studies = project.studies(:include => [:user, :roles], :order => 'created_at desc')
-
else
-
@studies = Study.newest_first.with_user_included.with_related_users_included
-
end
-
end
-
-
1
def index
-
# Please do not user current_user outside this block, you kill the API calls
-
setup_studies_from_scope(@exclude_nested_resource)
-
respond_to do |format|
-
format.html
-
format.xml { render(:action => (@exclude_nested_resource ? 'index' : 'index_deprecated_xml')) }
-
format.json { render :json => Study.all.to_json }
-
end
-
end
-
-
1
def study_list
-
return redirect_to(studies_path) unless request.xhr?
-
setup_studies_from_scope
-
render :partial => "study_list", :locals => { :studies => @studies.with_related_users_included.all }
-
end
-
-
1
def new
-
@study = Study.new
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
## Create the Study from new with the details from its form.
-
## Redirect to the index page with a notice.
-
1
def create
-
ActiveRecord::Base.transaction do
-
@study = Study.new(params['study'].merge(:user => current_user))
-
@study.save!
-
current_user.has_role('manager', @study)
-
User.find(params[:study_owner_id]).has_role('owner', @study) unless params[:study_owner_id].blank?
-
end
-
-
flash[:notice] = "Your study has been created"
-
respond_to do |format|
-
format.html { redirect_to study_path(@study) }
-
format.xml { render :xml => @study, :status => :created, :location => @study }
-
format.json { render :json => @study, :status => :created, :location => @study }
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
flash.now[:error] = "Problems creating your new study"
-
respond_to do |format|
-
format.html { render :action => "new" }
-
format.xml { render :xml => @study.errors, :status => :unprocessable_entity }
-
format.json { render :json => @study.errors, :status => :unprocessable_entity }
-
end
-
end
-
-
1
def show
-
@study = Study.find(params[:id])
-
flash.keep
-
respond_to do |format|
-
format.html do
-
if current_user.workflow.nil?
-
flash[:notice] = "Your profile is incomplete. Please select a workflow."
-
redirect_to edit_profile_path(current_user)
-
else
-
redirect_to study_workflow_path(@study, current_user.workflow)
-
end
-
end
-
format.xml { cache_xml_response(@study) }
-
format.json { render :json => @study.to_json }
-
end
-
end
-
-
1
def edit
-
@study = Study.find(params[:id])
-
flash.now[:warning] = @study.warnings if @study.warnings.present?
-
@users = User.all
-
redirect_if_not_owner_or_admin
-
end
-
-
1
def update
-
@study = Study.find(params[:id])
-
redirect_if_not_owner_or_admin
-
-
ActiveRecord::Base.transaction do
-
@study.update_attributes!(params[:study])
-
unless params[:study_owner_id].blank?
-
owner = User.find(params[:study_owner_id])
-
unless owner.is_owner?(@study)
-
@study.owners.first.has_no_role('owner', @study) if @study.owners.size == 1
-
owner.has_role('owner', @study)
-
end
-
end
-
-
flash[:notice] = "Your study has been updated"
-
-
redirect_to study_path(@study)
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
Rails.logger.warn "Failed to update attributes: #{@study.errors.map {|e| e.to_s }}"
-
flash.now[:error] = "Failed to update attributes for study!"
-
render :action => "edit", :id => @study.id
-
end
-
-
1
def study_status
-
@study = Study.find(params[:id])
-
redirect_if_not_owner_or_admin
-
-
if @study.inactive? || @study.pending?
-
@study.activate!
-
elsif @study.active?
-
@study.deactivate!
-
end
-
flash[:notice] = "Study status was updated successfully"
-
redirect_to study_path(@study)
-
end
-
-
1
def assembly
-
@study = Study.find(params[:id])
-
if params[:assembly]
-
unless @study.add_reference(params)
-
flash[:error] = "Failed to attach reference sequence"
-
else
-
redirect_to study_path(@study)
-
end
-
end
-
end
-
-
1
def properties
-
@study = Study.find(params[:id])
-
-
respond_to do |format|
-
format.html
-
format.xml
-
format.json { render :json => @study.to_json }
-
end
-
end
-
-
1
def collaborators
-
@study = Study.find(params[:id])
-
@all_roles = Role.all(:select => " distinct `name`")
-
@roles = Role.find(:all, :conditions => {:authorizable_id => @study.id, :authorizable_type => "Study"})
-
@users = User.all(:order => :first_name)
-
end
-
-
1
def related_studies
-
@study = Study.find(params[:id])
-
@relation_names = StudyRelationType::names
-
@studies = current_user.interesting_studies
-
@studies.reject {|s| s == @study }
-
-
#TODO create a proper ReversedStudyRelation
-
@relations = @study.study_relations.map { |r| [r.related_study, r.name ] } +
-
@study.reversed_study_relations.map { |r| [r.study, r.reversed_name ] }
-
-
end
-
-
-
1
def update_study_relation
-
@study = Study.find(params[:id])
-
status = 500
-
-
if pr=params[:related_study]
-
relation_type_name = pr[:relation_type]
-
related_study = Study.find_by_id pr[:study_id]
-
-
begin
-
yield(relation_type_name, related_study)
-
redirect_to :action => "related_studies"
-
return
-
rescue ActiveRecord::RecordInvalid, RuntimeError => ex
-
status = 403
-
flash.now[:error] = ex.to_s
-
end
-
-
else
-
flash.now[:error] = "A problem occurred while relating the study"
-
status = 500
-
end
-
@study.reload
-
related_studies
-
render :action => :related_studies, :status => status
-
end
-
-
1
def relate_study
-
update_study_relation do |relation_type_name, related_study|
-
StudyRelationType::relate_studies_by_name!(relation_type_name, @study, related_study)
-
flash[:notice] = "Relation added"
-
end
-
end
-
-
1
def unrelate_study
-
update_study_relation do |relation_type_name, related_study|
-
StudyRelationType::unrelate_studies_by_name!(relation_type_name, @study, related_study)
-
flash[:notice] = "Relation removed"
-
end
-
end
-
-
1
def follow
-
@study = Study.find(params[:id])
-
if current_user.has_role? 'follower', @study
-
current_user.has_no_role 'follower', @study
-
flash[:notice] = "You have stopped following the '#{@study.name}' study."
-
else
-
current_user.has_role 'follower', @study
-
flash[:notice] = "You are now following the '#{@study.name}' study."
-
end
-
redirect_to study_workflow_path(@study, current_user.workflow)
-
end
-
-
1
def choice
-
if params[:id]
-
kind = params[:id]
-
@curated_references = Sequence.curated_sequences
-
render :partial => "studies/sequences/" +kind.downcase
-
else
-
render :nothing => true
-
end
-
end
-
-
1
def close
-
@study = Study.find(params[:id])
-
@study.deactivate!
-
@study.save
-
flash[:notice] = "This study has been deactivated"
-
redirect_to study_path(@study)
-
end
-
-
1
def open
-
@study = Study.find(params[:id])
-
@study.activate!
-
@study.save
-
flash[:notice] = "This study has been activated"
-
redirect_to study_path(@study)
-
end
-
-
1
def show_accession
-
@study = Study.find(params[:id])
-
respond_to do |format|
-
xml_text =@study.accession_service.accession_study_xml(@study)
-
format.xml { render(:text => xml_text) }
-
end
-
end
-
-
1
def show_policy_accession
-
@study = Study.find(params[:id])
-
respond_to do |format|
-
xml_text =@study.accession_service.accession_policy_xml(@study)
-
format.xml { render(:text => xml_text) }
-
end
-
end
-
-
1
def show_dac_accession
-
@study = Study.find(params[:id])
-
respond_to do |format|
-
xml_text =@study.accession_service.accession_dac_xml(@study)
-
format.xml { render(:text => xml_text) }
-
end
-
end
-
-
1
def rescue_accession_errors
-
yield
-
rescue ActiveRecord::RecordInvalid => exception
-
flash[:error] = 'Please fill in the required fields'
-
render(:action => :edit)
-
rescue AccessionService::NumberNotRequired => exception
-
flash[:warning] = exception.message || 'An accession number is not required for this study'
-
redirect_to(study_path(@study))
-
rescue AccessionService::NumberNotGenerated => exception
-
flash[:warning] = 'No accession number was generated'
-
redirect_to(study_path(@study))
-
rescue AccessionService::AccessionServiceError => exception
-
flash[:error] = exception.message
-
redirect_to(edit_study_path(@study))
-
end
-
1
def accession
-
rescue_accession_errors do
-
@study = Study.find(params[:id])
-
@study.validate_ena_required_fields!
-
@study.accession_service.submit_study_for_user(@study, current_user)
-
-
flash[:notice] = "Accession number generated: #{ @study.ebi_accession_number }"
-
redirect_to(study_path(@study))
-
end
-
end
-
-
1
def dac_accession
-
rescue_accession_errors do
-
@study = Study.find(params[:id])
-
@study.accession_service.submit_dac_for_user(@study, current_user)
-
-
flash[:notice] = "Accession number generated: #{ @study.dac_accession_number }"
-
redirect_to(study_path(@study))
-
end
-
end
-
-
1
def policy_accession
-
rescue_accession_errors do
-
@study = Study.find(params[:id])
-
@study.accession_service.submit_policy_for_user(@study, current_user)
-
-
flash[:notice] = "Accession number generated: #{ @study.policy_accession_number }"
-
redirect_to(study_path(@study))
-
end
-
end
-
-
1
def sra
-
@study = Study.find(params[:id])
-
end
-
-
1
def state
-
@study = Study.find(params[:id])
-
end
-
-
1
def new_plate_submission
-
@study = Study.find(params[:id])
-
end
-
-
1
def create_plate_submission
-
@study = Study.find(params[:id])
-
@project = Project.find(params[:studies][:project])
-
-
plates = []
-
params[:studies][:barcodes].scan(/\d+/).each do |plate_barcode|
-
plate = Plate.find_by_barcode(plate_barcode)
-
unless plate.nil?
-
plates << plate
-
else
-
@study.errors.add("Plate", "Couldnt find plate #{plate_barcode}")
-
end
-
end
-
-
if @study.errors.count > 0
-
flash[:error] = "Error submitting your plates"
-
respond_to do |format|
-
format.html { render :action => "new_plate_submission"}
-
format.xml { render :xml => flash, :status => :unprocessable_entity }
-
format.json { render :json => flash, :status => :unprocessable_entity }
-
end
-
return
-
else
-
Plate.create_plates_submission(@project, @study, plates, current_user)
-
end
-
-
if @study.errors.count > 0
-
flash[:error] = "Error submitting your plates"
-
respond_to do |format|
-
format.html { render :action => "new_plate_submission"}
-
format.xml { render :xml => flash, :status => :unprocessable_entity }
-
format.json { render :json => flash, :status => :unprocessable_entity }
-
end
-
else
-
flash[:notice] = "Your plates have been submitted"
-
respond_to do |format|
-
format.html { render :action => "new_plate_submission" }
-
format.xml { render :xml => @study, :status => :created, :location => @study }
-
format.json { render :json => @study, :status => :created, :location => @study }
-
end
-
end
-
end
-
-
1
def self.role_helper(name, success_action, error_action, &block)
-
2
define_method("#{name}_role") do
-
ActiveRecord::Base.transaction do
-
@user, @study = User.find(params[:role][:user]), Study.find(params[:id])
-
-
if request.xhr?
-
if params[:role]
-
block.call(@user, @study, params[:role][:authorizable_type].to_s)
-
status, flash[:notice] = 200, "Role #{success_action}"
-
else
-
status, flash[:error] = 500, "A problem occurred while #{error_action} the role"
-
end
-
else
-
status, flash[:error] = 401, "A problem occurred while #{error_action} the role"
-
end
-
-
@roles = @study.roles(true).all
-
render :partial => "roles", :status => status
-
end
-
end
-
end
-
-
1
role_helper(:grant, "added", "adding") { |user,study,name| user.has_role(name, study) }
-
1
role_helper(:remove, "remove", "removing") { |user,study,name| user.has_no_role(name, study) }
-
-
1
def projects
-
@study = Study.find(params[:id])
-
@projects = @study.projects.paginate :page => params[:page]
-
end
-
-
1
def sample_manifests
-
@study = Study.find(params[:id])
-
@sample_manifests = @study.sample_manifests.paginate(:page => params[:page])
-
end
-
-
1
def suppliers
-
@study = Study.find(params[:id])
-
@suppliers = @study.suppliers.paginate(:page => params[:page])
-
end
-
-
1
def study_reports
-
@study = Study.find(params[:id])
-
@study_reports = StudyReport.for_study(@study).paginate(:page => params[:page], :order => 'id DESC')
-
end
-
-
-
1
private
-
-
1
def redirect_if_not_owner_or_admin
-
unless current_user.owner?(@study) or current_user.is_administrator? or current_user.is_manager?
-
flash[:error] = "Study details can only be altered by the owner (#{@study.user.login}) or an administrator or manager"
-
redirect_to study_path(@study)
-
end
-
end
-
-
1
def studies_from_scope(scope)
-
studies = case scope
-
when "interesting" then Study.of_interest_to(current_user)
-
when "followed" then Study.followed_by(current_user)
-
when "managed & active" then Study.managed_by(current_user).is_active
-
when "managed & inactive" then Study.managed_by(current_user).is_inactive
-
when "pending" then Study.is_pending
-
when "pending ethical approval" then Study.awaiting_ethical_approval
-
when "contaminated with human dna" then Study.contaminated_with_human_dna
-
when "remove x and autosomes" then Study.with_remove_x_and_autosomes
-
when "active" then Study.is_active
-
when "inactive" then Study.is_inactive
-
when "collaborations" then Study.collaborated_with(current_user)
-
when "all" then Study
-
else raise StandardError, "Unknown scope '#{ scope }'" # Study.of_interest_to(current_user)
-
end
-
-
return studies.newest_first
-
end
-
-
1
def rescue_validation
-
begin
-
yield
-
rescue ActiveRecord::RecordInvalid
-
Rails.logger.warn "Failed to update attributes: #{@study.errors.map {|e| e.to_s }}"
-
flash[:error] = "Failed to update attributes for study!"
-
render :action => "edit", :id => @study.id
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class StudyReportsController < ApplicationController
-
1
before_filter :login_required
-
-
1
def index
-
@study_reports = StudyReport.paginate(:page => params[:page], :order => "id desc")
-
@studies = Study.all(:order => "name ASC")
-
end
-
-
1
def new
-
params[:study_report] = {:study => params[:study]}
-
create
-
end
-
-
1
def create
-
study = Study.find_by_id(params[:study_report][:study])
-
study_report = StudyReport.create!(:study => study, :user => @current_user)
-
-
study_report.perform
-
-
respond_to do |format|
-
if study_report
-
flash[:notice] = "Report being generated"
-
format.html { redirect_to( study_reports_path ) }
-
format.xml { render :xml => study_report, :status => :created, :location => study_report }
-
format.json { render :json => study_report, :status => :created, :location => study_report }
-
else
-
flash[:error] = "Error: report not being generated"
-
format.html { redirect_to( study_reports_path ) }
-
format.xml { render :xml => flash[:error], :status => :unprocessable_entity }
-
format.json { render :json => flash[:error], :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def show
-
study_report = StudyReport.find(params[:id])
-
send_data( study_report.report.read, :type => "text/plain",
-
:filename=>"#{study_report.study.dehumanise_abbreviated_name}_progress_report.csv",
-
:disposition => 'attachment')
-
end
-
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
-
1
class SubmissionsController < ApplicationController
-
-
1
before_filter :lab_manager_login_required, :only => [:change_priority]
-
-
1
after_filter :set_cache_disabled!, :only => [:new, :index]
-
-
1
def new
-
self.expires_now
-
@presenter = Submission::SubmissionCreator.new(current_user, :study_id => params[:study_id])
-
end
-
-
1
def create
-
@presenter = Submission::SubmissionCreator.new(current_user, params[:submission])
-
-
if @presenter.save
-
render :partial => 'saved_order',
-
:locals => {
-
:presenter => @presenter,
-
:order => @presenter.order,
-
:form => :dummy_form_symbol
-
},
-
:layout => false
-
else
-
render :partial => 'order_errors', :layout => false, :status => 422
-
end
-
-
end
-
-
1
def edit
-
@presenter = Submission::SubmissionCreator.new(current_user, :id => params[:id] )
-
end
-
-
# This method will build a submission then redirect to the submission on completion
-
1
def update
-
@presenter = Submission::SubmissionCreator.new(current_user, :id => params[:id])
-
-
@presenter.build_submission!
-
-
flash[:error] = "The submission could not be built: #{@presenter.submission.errors.full_messages}" if @presenter.submission.errors.present?
-
-
redirect_to @presenter.submission
-
end
-
-
1
def change_priority
-
Submission.find(params[:id]).update_attributes!(:priority=>params[:submission][:priority])
-
redirect_to :action=>:show, :id=>params[:id]
-
end
-
-
1
def index
-
# Disable cache of this page
-
self.expires_now
-
-
@building = Submission.building.find(:all, :order => "created_at DESC", :conditions => { :user_id => current_user.id })
-
@pending = Submission.pending.find(:all, :order => "created_at DESC", :conditions => { :user_id => current_user.id })
-
@ready = Submission.ready.find(:all, :limit => 10, :order => "created_at DESC", :conditions => { :user_id => current_user.id })
-
end
-
-
1
def cancel
-
submission = Submission.find(params[:id])
-
submission.cancel!
-
redirect_to :action=>:show, :id=>params[:id]
-
end
-
-
1
def destroy
-
ActiveRecord::Base.transaction do
-
submission = Submission::SubmissionPresenter.new(current_user, :id => params[:id])
-
if submission.destroy
-
flash[:notice] = "Submission successfully deleted!"
-
else
-
flash[:error] = "This submission can't be deleted"
-
end
-
redirect_to :action => :index
-
end
-
end
-
-
1
def show
-
@presenter = Submission::SubmissionPresenter.new(current_user, :id => params[:id])
-
end
-
-
1
def study
-
@study = Study.find(params[:id])
-
@submissions = @study.submissions
-
end
-
-
################################################### AJAX ROUTES
-
# TODO[sd9]: These AJAX routes could be re-factored
-
1
def order_fields
-
@presenter = Submission::SubmissionCreator.new(current_user, params[:submission])
-
-
render :partial => 'order_fields', :layout => false
-
end
-
-
1
def study_assets
-
@presenter = Submission::SubmissionCreator.new(current_user, params[:submission])
-
-
render :partial => 'study_assets', :layout => false
-
end
-
################################################## End of AJAX ROUTES
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class TagGroupsController < ApplicationController
-
1
before_filter :admin_login_required, :only => [:new, :edit, :create, :update]
-
-
1
def index
-
@tag_groups = TagGroup.find(:all)
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def show
-
@tag_group = TagGroup.find(params[:id])
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def new
-
@number_of_tags = params[:number_of_tags]
-
@tag_group = TagGroup.new
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def edit
-
@tag_group = TagGroup.find(params[:id])
-
end
-
-
1
def create
-
@tag_group = TagGroup.new(params[:tag_group])
-
@tags = @tag_group.create_tags(params[:tags])
-
-
respond_to do |format|
-
if @tag_group.save
-
flash[:notice] = 'Tag Group was successfully created.'
-
format.html { redirect_to(@tag_group) }
-
else
-
format.html { redirect_to(@tag_group) }
-
end
-
end
-
end
-
-
1
def update
-
@tag_group = TagGroup.find(params[:id])
-
-
respond_to do |format|
-
if @tag_group.update_attributes(params[:tag_group])
-
flash[:notice] = 'Tag Group was successfully updated.'
-
format.html { redirect_to(@tag_group) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014 Genome Research Ltd.
-
1
class TagsController < ApplicationController
-
1
before_filter :admin_login_required, :only => [:edit, :update]
-
1
before_filter :find_tag_group
-
1
before_filter :find_tag_by_id, :only => [:show, :edit, :update]
-
-
1
def show
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
-
if @tag.update_attributes(params[:tag])
-
flash[:notice] = 'Tag was successfully updated.'
-
format.html { redirect_to(@tag_group) }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
1
private
-
1
def find_tag_group
-
@tag_group = TagGroup.find(params[:tag_group_id])
-
end
-
-
1
def find_tag_by_id
-
@tag = Tag.find(params[:id])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class TasksController < ApplicationController
-
1
before_filter :find_tasks_by_id, :only => [:show, :edit, :update, :destroy]
-
-
1
def index
-
@tasks = Task.find(:all)
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @tasks.to_xml }
-
end
-
end
-
-
1
def show
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @task.to_xml }
-
end
-
end
-
-
1
def new
-
@task = SetDescriptorsTask.new
-
@workflow = LabInterface::Workflow.find(params[:workflow_id])
-
@task.descriptors << Descriptor.new({ :name => '', :value => ''})
-
@count = @task.descriptors.size
-
end
-
-
1
def new_field
-
render :partial => 'descriptor', :locals => { :field => Descriptor.new, :field_number => params[:id] }
-
end
-
-
1
def new_option
-
render :partial => 'option', :locals => { :field => Descriptor.new, :field_number => params[:id], :option_number => params[:option], :name => '' }
-
end
-
-
1
def edit
-
@count = @task.descriptors.size
-
end
-
-
1
def create
-
params[:task][:pipeline_workflow_id] = params[:task].delete(:workflow_id)
-
@task = SetDescriptorsTask.new(params[:task])
-
-
respond_to do |format|
-
if @task.save
-
@task.create_descriptors(params[:descriptor])
-
-
flash[:notice] = 'Task was successfully created.'
-
format.html { redirect_to task_url(@task) }
-
format.xml { head :created, :location => task_url(@task) }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @task.errors.to_xml }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @task.update_attributes(params[:task])
-
@task.update_descriptors(params[:descriptor])
-
-
flash[:notice] = 'Task was successfully updated.'
-
format.html { redirect_to task_url(@task) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @task.errors.to_xml }
-
end
-
end
-
end
-
-
1
def destroy
-
@task.destroy
-
-
respond_to do |format|
-
format.html { redirect_to tasks_url }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def find_tasks_by_id
-
@task = Task.find(params[:id])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014 Genome Research Ltd.
-
1
class UsersController < ApplicationController
-
-
1
before_filter :validate_user, :except => [:index, :projects, :study_reports]
-
1
before_filter :find_user, :except => [:index]
-
-
1
def index
-
@users = User.all
-
end
-
-
1
def show
-
end
-
-
1
def edit
-
end
-
-
1
def update
-
params[:user].delete(:swipecard_code) if params[:user][:swipecard_code].blank?
-
@user = User.find(params[:id])
-
if @user.id == params[:id].to_i
-
@user.update_attributes(params[:user])
-
end
-
if @user.save
-
flash[:notice] = "Profile updated"
-
else
-
flash[:error] = "Problem updating profile."
-
end
-
redirect_to :action => :show, :id => @user.id
-
end
-
-
1
def projects
-
@projects = @user.projects.paginate :page => params[:page]
-
end
-
-
1
def study_reports
-
@study_reports = StudyReport.for_user(@user).paginate(:page => params[:page], :order => "id desc")
-
end
-
-
1
private
-
-
1
def validate_user
-
if current_user.administrator? || current_user.id == params[:id].to_i
-
return true
-
else
-
flash[:error] = "You don't have permission to view or edit that profile: here is yours instead."
-
redirect_to :action => :show, :id => current_user.id
-
end
-
end
-
-
1
def find_user
-
@user = User.find(params[:id])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015,2015 Genome Research Ltd.
-
1
class WorkflowsController < ApplicationController
-
1
before_filter :find_workflow_by_id, :only =>[:auto_batch, :show, :edit, :duplicate, :batches, :update, :destroy, :reorder_tasks]
-
-
1
include Tasks::AddSpikedInControlHandler
-
1
include Tasks::AssignTagsHandler
-
1
include Tasks::AssignTagsToWellsHandler
-
1
include Tasks::AssignTagsToTubesHandler
-
1
include Tasks::AssignTubesToWellsHandler
-
1
include Tasks::AttachInfiniumBarcodeHandler
-
1
include Tasks::BindingKitBarcodeHandler
-
1
include Tasks::CherrypickGroupBySubmissionHandler
-
1
include Tasks::CherrypickHandler
-
1
include Tasks::DnaQcHandler
-
1
include Tasks::GenerateManifestHandler
-
1
include Tasks::MovieLengthHandler
-
1
include Tasks::PlateTemplateHandler
-
1
include Tasks::PlateTransferHandler
-
1
include Tasks::PrepKitBarcodeHandler
-
1
include Tasks::ReferenceSequenceHandler
-
1
include Tasks::SamplePrepQcHandler
-
1
include Tasks::SetDescriptorsHandler
-
1
include Tasks::SetCharacterisationDescriptorsHandler
-
1
include Tasks::SetLocationHandler
-
1
include Tasks::TagGroupHandler
-
1
include Tasks::ValidateSampleSheetHandler
-
1
include Tasks::StartBatchHandler
-
1
include Tasks::StripTubeCreationHandler
-
-
1
def index
-
@workflows = LabInterface::Workflow.all
-
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @workflows.to_xml}
-
end
-
end
-
-
1
def auto_batch
-
@batch = Batch.find(params[:batch_id])
-
end
-
-
1
public
-
-
1
def show
-
respond_to do |format|
-
format.html
-
format.xml { render :xml => @workflow.to_xml }
-
end
-
end
-
-
1
def new
-
@workflow = LabInterface::Workflow.new
-
end
-
-
1
def edit
-
end
-
-
1
def duplicate
-
if @workflow.deep_copy
-
flash[:notice] = 'Workflow was successfully duplicated.'
-
else
-
flash[:error] = 'Something has gone wrong.'
-
end
-
respond_to do |format|
-
format.html { redirect_to workflows_url }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def batches
-
@workflow = LabInterface::Workflow.find(params[:id])
-
#TODO association broken here - something to do with the attachables polymorph?
-
@batches = Batch.find_all_by_workflow_id(@workflow.id).sort_by {|batch| batch.id}.reverse
-
end
-
-
1
def create
-
@workflow = LabInterface::Workflow.new(params[:workflow])
-
-
respond_to do |format|
-
if @workflow.save
-
flash[:notice] = 'Workflow was successfully created.'
-
format.html { redirect_to workflow_url(@workflow) }
-
format.xml { head :created, :location => workflow_url(@workflow) }
-
else
-
format.html { render :action => "new" }
-
format.xml { render :xml => @workflow.errors.to_xml }
-
end
-
end
-
end
-
-
1
def update
-
respond_to do |format|
-
if @workflow.update_attributes(params[:workflow])
-
flash[:notice] = 'Workflow was successfully updated.'
-
format.html { redirect_to workflow_url(@workflow) }
-
format.xml { head :ok }
-
else
-
format.html { render :action => "edit" }
-
format.xml { render :xml => @workflow.errors.to_xml }
-
end
-
end
-
end
-
-
1
def destroy
-
@workflow.destroy
-
-
respond_to do |format|
-
format.html { redirect_to workflows_url }
-
format.xml { head :ok }
-
end
-
end
-
-
1
def reorder_tasks
-
end
-
-
1
def sort
-
@workflow = LabInterface::Workflow.find(params[:workflow_id])
-
@task_list = @workflow.tasks
-
@task_list.each do |task|
-
task.sorted = params['task_list'].index(task.id.to_s) + 1
-
task.save
-
end
-
render :nothing => true
-
end
-
-
# TODO: This needs to be made RESTful.
-
# 1: Routes need to be refactored to provide more sensible urls
-
# 2: We call them tasks in the code, and stages in the URL. They should be consistent
-
# 3: This endpoint currently does two jobs, executing the current task, and rendering the next
-
# 4: Some tasks rely on parameters passed in from the previous task. This isn't ideal, but it might
-
# be worth maintaining the behaviour until we solve the problems.
-
# 5: We need to improve the repeatability of tasks.
-
1
def stage
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
@stage = params[:id].to_i
-
@task = @workflow.tasks[@stage]
-
-
-
ActiveRecord::Base.transaction do
-
# If params[:next_stage] is nil then just render the current task
-
# else actually execute the task.
-
unless params[:next_stage].nil?
-
-
eager_loading = @task.included_for_do_task
-
@batch ||= Batch.find(params[:batch_id], :include => eager_loading )
-
unless @batch.editable?
-
flash[:error] = "You cannot make changes to a completed batch."
-
redirect_to :back
-
return false
-
end
-
-
if @task.do_task(self, params)
-
# Task completed, start the batch is necessary and display the next one
-
do_start_batch_task(@task,params)
-
@stage += 1
-
params[:id] = @stage
-
@task = @workflow.tasks[@stage]
-
end
-
end
-
-
# Is this the last task in the workflow?
-
if @stage >= @workflow.tasks.size
-
# All requests have finished all tasks: finish workflow
-
redirect_to finish_batch_url(@batch)
-
else
-
if @batch.nil? || @task.included_for_render_task != eager_loading
-
@batch = Batch.find(params[:batch_id], :include => @task.included_for_render_task )
-
end
-
@task.render_task(self, params)
-
end
-
end
-
end
-
-
1
def render_task(task, params)
-
@rits = @batch.pipeline.request_information_types
-
@requests = @batch.requests
-
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
@task = task
-
end
-
-
1
private
-
-
1
def ordered_fields(fields)
-
response = Array.new
-
fields.keys.sort_by{|key| key.to_i}.each do |key|
-
response.push fields[key]
-
end
-
response
-
end
-
-
1
def flatten_hash(hash = params, ancestor_names = [])
-
flat_hash = {}
-
hash.each do |k, v|
-
names = Array.new(ancestor_names)
-
names << k
-
if v.is_a?(Hash)
-
flat_hash.merge!(flatten_hash(v, names))
-
else
-
key = flat_hash_key(names)
-
key += "[]" if v.is_a?(Array)
-
flat_hash[key] = v
-
end
-
end
-
-
flat_hash
-
end
-
-
1
def flat_hash_key(names)
-
names = Array.new(names)
-
name = names.shift.to_s.dup
-
names.each do |n|
-
name << "[#{n}]"
-
end
-
name
-
end
-
-
1
def find_workflow_by_id
-
@workflow = LabInterface::Workflow.find(params[:id])
-
end
-
-
1
def eventify_batch(batch, task)
-
event = batch.lab_events.create({})
-
event.description = "Complete"
-
event.add_descriptor Descriptor.new({ :name => 'task_id', :value => task.id })
-
event.add_descriptor Descriptor.new({ :name => 'task', :value => task.name })
-
event.batch = batch
-
event.user = current_user
-
event.save
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module ApplicationHelper
-
-
# Should return either the custom text or a blank string
-
1
def custom_text(identifier, differential = nil)
-
1
Rails.cache.fetch("#{identifier}-#{differential}") do
-
custom_text = CustomText.first(
-
:conditions => {
-
:identifier => identifier,
-
:differential => differential
-
}
-
)
-
-
#.debug
-
# "No custom text found for #{identifier} #{differential}." if custom_text.nil?
-
-
custom_text.try(:content) || ""
-
end
-
-
end
-
-
1
def loading_bar(identifier = "loading")
-
content_tag("div", :id => identifier, :class => "loading_bar", :style => "display:none") do
-
image_tag "loader-bar.gif", :size => "200x19"
-
end
-
end
-
-
1
def remote_error(identifier = "remote_error")
-
content_tag("div", :id => identifier, :class=>"error", :style => "display:none;") do
-
"An error has occurred and the results can not be shown at the moment"
-
end
-
end
-
-
1
def display_for_setting(setting)
-
display = true
-
if logged_in?
-
if current_user.setting_for? setting
-
if current_user.value_for(setting) == "hide"
-
display = false
-
end
-
end
-
end
-
display
-
end
-
-
1
def required_marker
-
content_tag(:span,"»".html_safe,:class=>'required')
-
end
-
-
1
def render_flashes
-
1
output = String.new.html_safe
-
1
flash.to_a.each do |key, message|
-
content = message
-
content = message.map { |m| content_tag(:div, m) }.join if message.is_a?(Array)
-
output << alert(key,:id=>"message_#{key}") { content }
-
end
-
1
return output
-
end
-
-
1
def api_data
-
{:api_version => RELEASE.api_version}
-
end
-
-
1
def display_user_guide(display_text, link=nil)
-
alert(:info) do
-
link.present? ? link_to(display_text, link) : display_text
-
end
-
end
-
-
1
def display_user_error(display_text, link=nil)
-
alert(:danger) do
-
link.present? ? link_to(display_text, link) : display_text
-
end
-
end
-
-
1
def display_status(status)
-
content_tag(:span,status,:class=>"request-state label label-#{bootstrapify_request_state(status)}")
-
end
-
-
1
def dynamic_link_to(summary_item)
-
object = summary_item.object
-
if object.instance_of?(Asset)
-
return link_to("#{object.name}", asset_path(object))
-
elsif object.instance_of?(Request)
-
return link_to("Request #{object.id}", request_path(object))
-
else
-
return 'No link available'
-
end
-
end
-
-
1
def request_count_link(study, asset, state, request_type)
-
matching_requests = asset.requests.select { |request| (request.request_type == request_type) and request.send(:"#{ state }?") }
-
html_options, count = { :title => "#{ asset.display_name } #{ state }" }, matching_requests.size
-
-
# 0 requests => no link, just '0'
-
# 1 request => request summary page
-
# N requests => summary overview
-
if count == 1
-
url_path = request_path(matching_requests.first)
-
link_to count, url_path, html_options
-
elsif count > 1
-
url_path = study_requests_path(study, :state => state, :request_type_id => request_type.id, :asset_id => asset.id)
-
link_to count, url_path, html_options
-
end
-
-
end
-
-
1
def request_link(object, count, request_type, status = nil, options = {}, link_options = {})
-
link_to_if((count != 0), count, request_list_path(object, request_type, status, options), link_options)
-
end
-
-
1
def request_list_path(object, request_type = nil, status = nil, options = {})
-
options[:state] = status unless status.nil?
-
options[:request_type_id] = request_type.id unless request_type.nil?
-
-
if object.instance_of?(Asset)
-
asset_path(object, options)
-
elsif object.instance_of?(Study)
-
study_requests_path(object, options)
-
end
-
end
-
-
1
def display_follow(item, user, msg)
-
if user.following?(item)
-
"Unfollow " + msg
-
else
-
"Follow " + msg
-
end
-
end
-
-
1
def progress_bar(count)
-
color = ""
-
if count < 25
-
color = "ccaaaa"
-
elsif count > 99
-
color = "aaddaa"
-
else
-
color = "DAEE34"
-
end
-
-
# TODO: Refactor this to use the bootstrap styles
-
content_tag(:span,count,style:"display:none") <<
-
content_tag(:div,style:"width: 100px; background-color: #CCCCCC; color: inherit;") do
-
content_tag(:div,"#{count}%",style:"width: #{count}px; background-color: ##{color}; color: inherit; text-align:center")
-
end
-
end
-
-
1
def completed(object, request_type = nil, cache = {})
-
-
total = 0
-
passed = 0
-
failed = 0
-
-
if request_type
-
-
unless cache.blank?
-
passed_cache = cache[:passed]
-
failed_cache = cache[:failed]
-
total_cache = cache[:total]
-
-
total = total_cache[request_type][object.id]
-
passed = passed_cache[request_type][object.id]
-
failed = failed_cache[request_type][object.id]
-
-
else
-
total = object.requests.request_type(request_type).size
-
passed = object.requests.request_type(request_type).passed.count
-
failed = object.requests.request_type(request_type).failed.count
-
end
-
else
-
total = object.requests.request_type(request_type).size
-
passed = object.requests.passed.size
-
failed = object.requests.failed.size
-
end
-
-
if (total - failed) > 0
-
return ((passed.to_f / (total - failed).to_f)*100).to_i
-
else
-
return 0
-
end
-
end
-
-
1
def study_state(state)
-
if state == "active"
-
return "<span style='color:green;'>#{state}</span>"
-
else
-
return "<span style='color:red;'>#{state}</span>"
-
end
-
end
-
-
1
def display_empty_table(display_text, link=nil)
-
unless link.nil?
-
content_tag(:div, link_to(display_text, link), :class => "empty_table", :id => "empty_table")
-
else
-
content_tag(:div, display_text, :class => "empty_table", :id => "empty_table")
-
end
-
end
-
-
-
## From Pipelines
-
-
1
def render_title(title = "")
-
add :title, title
-
end
-
-
1
def render_help(help = "")
-
add :help, help
-
end
-
-
1
def tabulated_error_messages_for(*params)
-
options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {}
-
objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact
-
count = objects.inject(0) {|sum, object| sum + object.errors.count }
-
unless count.zero?
-
error_messages = objects.map {|object| object.errors.full_messages.map {|msg| content_tag(:div, msg) } }.join
-
[content_tag(:td, :class=>'error item') do
-
"Your #{params.first} has not been created."
-
end,
-
content_tag(:td, :class=>'error') do
-
raw(error_messages)
-
end].join.html_safe
-
else
-
""
-
end
-
end
-
-
-
1
def horizontal_tab(name, key, related_div, tab_no, selected = false)
-
link_to raw("#{name}"), "javascript:void(0);", :'data-tab-refers' => "##{related_div}", :'data-tab-group' => tab_no, :id => "#{key}", :class => "#{selected ? "selected " : ""}tab#{tab_no}"
-
#link_to raw("#{name}"), "javascript:void(0);", :onclick => %Q{swap_tab("#{key}", "#{related_div}", "#{tab_no}");}, :id => "#{key}", :class => "#{selected ? "selected " : ""}tab#{tab_no}"
-
end
-
-
1
def item_status(item)
-
if item.failures.empty?
-
""
-
else
-
%Q{<span style="color:red;">FAILED</span>}
-
end
-
end
-
-
1
def display_complex_content(hash_content)
-
hash_content.each do |key, value|
-
case key
-
when "criterion"
-
output = ""
-
value.each do |v|
-
output = output + content_tag(:span, "<strong>#{v.inspect}</strong>")
-
output = output + content_tag(:br)
-
end
-
return output
-
when "link"
-
return link_to(value["label"], value["href"])
-
end
-
end
-
end
-
-
1
def display_ready_for_manual_qc(v)
-
if v
-
image_tag("accept.png")
-
else
-
image_tag("error.png")
-
end
-
end
-
-
1
def display_request_information(request, rit, batch = nil)
-
r = request.value_for(rit.name, batch)
-
(!r || r.empty?) ? "NA" : r
-
end
-
-
1
def display_boolean_results(result)
-
return "NA" if (!result || result.empty?)
-
if result == "pass" || result == "1" || result == "true"
-
return image_tag("accept.png", :title => result)
-
else
-
return image_tag("error.png", :title => result)
-
end
-
end
-
-
1
def sorted_requests_for_search(requests)
-
sorted_requests = requests.select{|r| r.pipeline_id.nil?}
-
new_requests = requests - sorted_requests
-
new_requests.sort_by(&:pipeline_id)
-
requests = requests + sorted_requests
-
end
-
-
1
def display_hash_value(hash, key, sub_key)
-
hash.fetch(key, {}).fetch(sub_key, '')
-
end
-
-
# Creates a label that is hidden from the view so that testing is easier
-
1
def hidden_label_tag_for_testing(name, text = nil, options = {})
-
label_tag(name, text, options.merge(:style => 'display:none;'))
-
end
-
-
1
def non_breaking_space
-
' '.html_safe
-
end
-
-
1
def help_text(label_text = nil, suggested_id = nil, &block)
-
content = capture(&block)
-
return if content.blank?
-
tooltip_id = "prop_#{suggested_id || content.hash}_help"
-
tooltip('?', :id => tooltip_id, &block)
-
end
-
-
# The admin email address should be stored in config.yml for the current environment
-
1
def help_email_link
-
1
admin_address = configatron.admin_email || "admin@test.com"
-
1
link_to "#{admin_address}", "mailto:#{admin_address}"
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module AssetsHelper
-
1
def well_identifier(plate_layout, row, column)
-
plate_layout.cell_name_for_well_at(row, column)
-
end
-
-
1
def well_information(plate_layout, row, column)
-
well = plate_layout.well_at(row, column)
-
if plate_layout.empty_well_at?(row, column)
-
["Empty", "", ""]
-
elsif plate_layout.good_well_at?(row, column)
-
["Request ID: #{well[:request].id}", "Asset: #{well[:asset].name}", "Barcode: #{well[:asset].barcode}"]
-
elsif plate_layout.bad_well_at?(row, column)
-
["Error", "#{well[:error]}", ""]
-
else
-
raise StandardError, "Unknown well status ((#{ plate_layout.location_for_well_at(row, column) }) = #{ plate_layout.well_at(row, column).inspect })"
-
end
-
end
-
-
1
def well_color(plate_layout, row, column)
-
if plate_layout.empty_well_at?(row, column)
-
"empty_cell"
-
elsif plate_layout.good_well_at?(row, column)
-
"good_cell"
-
else
-
"bad_cell"
-
end
-
end
-
-
# Returns an appropriate path given the current parameters
-
1
def new_request_asset_path_in_context(asset)
-
path_options = {}
-
path_options[:study_id] = params[:study_id] if params.key?(:study_id)
-
new_request_asset_path(path_options.merge(:id => asset.id))
-
end
-
-
# Given the core name of an instance variable or ID parameter this method yields the name of the ID
-
# parameter along with its current value, based either on the instance variable ID value or the
-
# ID parameter. For instance, if the 'name' is 'foo' then either the '@foo.id' value will be yielded,
-
# or the 'params[:foo_id]' value if @foo is nil.
-
1
def instance_variable_or_id_param(name, &block)
-
field_name, value = :"#{name}_id", instance_variable_get(:"@#{name}")
-
value_id = value.nil? ? params[field_name] : value.id
-
concat(capture(field_name, value_id, &block))
-
end
-
-
# Returns a select tag that has it's options ordered by name (assumes present of sorted_by_name function)
-
# and disabled if a value has been pre-selected.
-
1
def select_field_sorted_by_name(field, select_options_source, selected, options = {})
-
content_tag(:div, :class=>'col-md-5') do
-
select_tag(
-
field,
-
options_for_select(select_options_source.sorted_by_name.map { |x| [x.name, x.id] }, selected.try(:to_i)),
-
options.merge(:disabled => (selected.present? and not current_user.is_administrator?), :class=>'form-control select2')
-
)
-
end
-
end
-
-
# Returns true if the current user can request additional sequencing on the given asset, otherwise false
-
1
def current_user_can_request_additional_sequencing_on?(asset)
-
return false unless asset.is_sequenceable? # Asset must be sequenceable ...
-
return true if current_user.is_administrator? # ... user could be an administrator ...
-
return true if current_user.is_manager?
-
#asset.studies.any? { |study| current_user.is_manager?(study) } # ... or a manager of any study related to the asset
-
end
-
-
# Returns true if the current user can request an additional library on the asset, otherwise false
-
1
def current_user_can_request_additional_library_on?(asset)
-
asset.is_a?(SampleTube) and current_user.is_administrator?
-
end
-
-
1
def current_user_can_make_additional_requests_on?(asset, study)
-
return false unless study.present? # Study must be specified ...
-
return true if current_user.is_administrator? # ... user could be an administrator ...
-
current_user.is_manager?(study) # ... or the manager of the specified study
-
end
-
-
1
def current_user_studies_from(asset)
-
return Study if current_user.is_administrator?
-
-
# Bit of a hack in that we want to provide the same interface as would be seen if this were an
-
# ActiveRecord model rather than an array.
-
Study.all.select { |study| current_user.is_manager?(study) }.tap do |results|
-
def results.sorted_by_name
-
sort_by(&:name)
-
end
-
end
-
end
-
-
1
def asset_types
-
['All', *Aliquot::Receptacle.descendants.map(&:name)]
-
end
-
-
1
def asset_types_for_select
-
asset_types.map {|at| [at.underscore.humanize, at] }
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module BarcodePrintersHelper
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
module BatchesHelper
-
1
def purpose_for_plate(plate)
-
if plate.plate_purpose.nil? || plate.plate_purpose.name.blank?
-
"Unassigned"
-
else
-
plate.plate_purpose.name
-
end
-
end
-
-
1
def fluidigm_plate(plate)
-
plate.purpose.barcode_for_tecan == 'fluidigm_barcode'
-
end
-
-
# Used by both assets/show.xml.builder and batches/show.xml.builder
-
1
def output_aliquot(xml, aliquot)
-
xml.sample(
-
:sample_id => aliquot.sample.id,
-
:library_id => aliquot.library_id,
-
:library_name => aliquot.library.try(:name),
-
:library_type => aliquot.library_type,
-
:study_id => aliquot.study_id,
-
:project_id => aliquot.project_id,
-
:consent_withdrawn => aliquot.sample.consent_withdrawn?
-
) {
-
# NOTE: XmlBuilder has a method called 'tag' so we have to say we want the element 'tag'!
-
xml.tag!(:tag, :tag_id => aliquot.tag.id) {
-
xml.index aliquot.aliquot_index_value||aliquot.tag.map_id
-
xml.expected_sequence aliquot.tag.oligo
-
xml.tag_group_id aliquot.tag.tag_group_id
-
} unless aliquot.tag.nil?
-
-
xml.tag(:tag2_id => aliquot.tag2.id) {
-
xml.expected_sequence aliquot.tag2.oligo
-
xml.tag_group_id aliquot.tag2.tag_group_id
-
} unless aliquot.tag2.nil?
-
-
xml.bait(:id => aliquot.bait_library.id) {
-
xml.name aliquot.bait_library.name
-
} if aliquot.bait_library.present?
-
-
xml.insert_size(:from => aliquot.insert_size.from, :to => aliquot.insert_size.to) if aliquot.insert_size.present?
-
}
-
end
-
-
1
def workflow_name(batch)
-
return unless batch and batch.workflow
-
batch.workflow.name.gsub(/Cluster formation | \([^\)]*\)/,'')
-
end
-
-
1
def batch_link(batch,options)
-
link_text = content_tag(:strong,"Batch #{batch.id} ") <<
-
content_tag(:span,batch.pipeline.name,:class=>'pipline-name') << ' ' <<
-
content_tag(:span,batch.state,:class=>"batch-state label label-#{bootstrapify_batch_state(batch.state)}")
-
link_to(link_text, batch_path(batch), options)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module BootstrapHelper
-
-
1
def panel(type=:default,options={},&block)
-
2
bs_custom_panel(type,:div,{:class=>"panel-body"},options,&block)
-
end
-
-
1
def list_panel(type=:default,options={},&block)
-
bs_custom_panel(type,:ul,{:class=>"list-group"},options,&block)
-
end
-
-
1
def link_panel(type=:default,options={},&block)
-
bs_custom_panel(type,:div,{:class=>"list-group"},options,&block)
-
end
-
-
1
def bs_custom_panel(type,body_type,body_options,options,&block)
-
2
title = options.delete(:title)
-
2
options[:class] ||= String.new
-
2
options[:class] << " panel panel-#{type}"
-
2
content_tag(:div,options) do
-
2
out = String.new.html_safe
-
out << content_tag(:div,:class=>"panel-heading") do
-
1
content_tag(:h3,title,:class=>"panel-title")
-
2
end unless title.nil?
-
2
out << content_tag(body_type,body_options,&block)
-
end
-
end
-
-
# <div class="alert alert-warning" role="alert">
-
# block_content
-
# </div>
-
1
def alert(type=:default,options={},&block)
-
bs_type = bootstrapify(type.to_s)
-
options[:class] ||= String.new
-
options[:role] ||= 'alert'
-
options[:class] << " alert alert-#{bs_type}"
-
content_tag(:div,options,&block)
-
end
-
-
# Summary composits a panel with a table to deliver
-
# a list of key-value pairs
-
# <div class="col-md-6">
-
# <div class="panel panel-default">
-
# <div class="panel-heading"><h3 class="panel-title">Summary</h3></div>
-
# <table class='table table-summary'>
-
# <tr>
-
# <th>Array[0][0]</th>
-
# <td>Array[0][1]</td>
-
# </tr>
-
# </table>
-
# </div>
-
# </div>
-
1
def summary(type=:default,options={},&block)
-
bs_type = bootstrapify(type.to_s)
-
title = options.delete(:title)||'Summary'
-
size = options.delete(:size)||'6'
-
options[:class] ||= String.new
-
options[:class] << " panel panel-#{bs_type}"
-
content_tag(:div, :class=>"col-md-#{size}") do
-
content_tag(:div,options) do
-
content_tag(:div,:class=>"panel-heading") do
-
content_tag(:h3,title,:class=>"panel-title")
-
end <<
-
content_tag(:table,:class=>"table table-summary") do
-
String.new.html_safe.tap do |rows|
-
yield.each do |key,value|
-
rows << content_tag(:tr) do
-
content_tag(:th,key) << content_tag(:td,value)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# <div class="page-header">
-
# <h1>Title <small>subtitle</small></h1>
-
# </div>
-
1
def page_title(title,subtitle=nil)
-
content_tag(:div, :class=>"page-header") do
-
content_tag(:h1) do
-
core = escape_once(title.titleize).html_safe
-
core << " " << content_tag(:small,subtitle) if subtitle.present?
-
core
-
end
-
end
-
end
-
-
1
def pagination(collection)
-
2
will_paginate collection, renderer: BootstrapPagination::Rails, previous_label: "«", next_label: "»"
-
end
-
-
#<div class="col-md-size form-group"></div>
-
1
def form_group(size=12,&block)
-
content_tag(:div,:class=>"form-group col-md-#{size}",&block)
-
end
-
-
1
def bs_column(size=6,screen='md',&block)
-
1
content_tag(:div,:class=>"col-#{screen}-#{size}",&block)
-
end
-
-
# def progress_bar(percent = 100, hidden=true, identifier = "loading")
-
# display = hidden ? "display:none" : nil
-
# content_tag(:div, :class=>'progress', :style => display,) do
-
# content_tag("div", :id => identifier, :class => "progress-bar progress-bar-striped active", :role => 'progressbar') do
-
# content_tag(:span,'Loading...',:class=>'sr-only')
-
# end
-
# end
-
# end
-
-
# <div class="progress">
-
# <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="width: 45%">
-
# <span class="sr-only">45% Complete</span>
-
# </div>
-
# </div>
-
1
def loading_bar(id='update_loader')
-
content_tag(:div,class:'loading-bar-placeholder') do
-
content_tag(:div,id:id,class:'progress loading-bar-container') do
-
content_tag(:div,'Loading',class:'progress-bar progress-bar-striped active loading-bar',role:'progressbar')
-
end
-
end
-
end
-
-
1
def render_section(form,field_name,sections,field)
-
form_group do
-
fg = content_tag(:div,:class=>"col-md-4") do
-
label = form.label(field_name, sections.label, sections.label_options)
-
label << content_tag(:br)
-
label << content_tag(:span, sections.edit_info, :class => 'property_edit_info') if sections.edit_info
-
end
-
fg << content_tag(:div,field,:class=>'col-md-5')
-
fg << content_tag(:div,:class=>'col-md-3') do
-
help_text("#{sections.label} help text", field.hash) do
-
raw(sections.help)
-
end if sections.help.present?
-
end
-
end
-
end
-
-
1
def form_collection(label,field,help=nil,friendly_label='Field')
-
form_group do
-
fg = bs_column(4,'md') { label }
-
fg << bs_column(5,'md') { field }
-
fg << bs_column(3,'md') do
-
help_text("#{friendly_label} help text") { raw(help)}
-
end if help
-
fg
-
end
-
end
-
-
1
def bs_select(*args)
-
hashes = args[-2,2].select {|arg| arg.respond_to?(:keys) }.count
-
(2 - hashes).times do
-
args << {}
-
end
-
args.last[:class] ||= ''
-
args.last[:class] << ' form-control'
-
select(*args)
-
end
-
-
-
1
def bootstrapify(level)
-
{
-
'notice' => 'success','error' => 'danger',
-
'pending' => 'muted', 'started'=> 'primary',
-
'passed' => 'success', 'failed' => 'danger',
-
'cancelled' => 'warning'
-
}[level]||level
-
end
-
-
-
1
def bootstrapify_request_state(state)
-
{
-
'completed' => 'info',
-
'discarded' => 'default',
-
'cancelled' => 'default',
-
'failed' => 'danger',
-
'pending' => 'warning',
-
'passed' => 'success',
-
'started' => 'primary'
-
}[state]||'default'
-
end
-
-
1
def bootstrapify_batch_state(state)
-
{
-
'completed' => 'info',
-
'discarded' => 'default',
-
'failed' => 'danger',
-
'pending' => 'warning',
-
'released' => 'success',
-
'started' => 'primary'
-
}[state]||'default'
-
end
-
-
-
1
def bootstrapify_study_state(state)
-
{
-
'pending' => 'warning',
-
'active' => 'success',
-
'inactive' => 'danger'
-
}[state.downcase]||'default'
-
end
-
-
1
def bootstrapify_submission_state(state)
-
{
-
'building' => 'info',
-
'cancelled' => 'default',
-
'failed' => 'danger',
-
'pending' => 'warning',
-
'processing' => 'primary',
-
'ready' => 'success'
-
}[state]||'default'
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module DelayedJobsHelper
-
1
def job_type(job)
-
if job.name =~ /StudyReport/
-
"generate study report"
-
elsif job.name =~ /Submission/
-
"process submission "
-
else
-
job.name
-
end
-
end
-
1
def job_status(job)
-
if job.locked_by
-
"In progress"
-
elsif job.failed?
-
"Failed"
-
elsif job.last_error?
-
"error"
-
else
-
"pending"
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module DescriptorJavascriptHelper
-
# The <tt>javascripts/descriptor.js</tt> needs a certain kind of setup to ensure that it behaves
-
# properly and so this is what this method generates in the HTML.
-
1
def descriptor_javascript_support(controller_name, model_name)
-
javascript_include_tag('descriptors') <<
-
javascript_tag("set_controller('#{ controller_name }'); set_model('#{ model_name }');") <<
-
hidden_field_tag(:count, @count)
-
end
-
-
1
def link_to_add_descriptor(&block)
-
link_to_with_onclick_only('addDescriptor();return false;', &block)
-
end
-
-
1
def link_to_remove_descriptor(index, &block)
-
link_to_with_onclick_only("removeDescriptor(#{ index });return false;", &block)
-
end
-
-
1
def link_to_add_asset(family, &block)
-
link_to_with_onclick_only("addAsset('#{ family.id }');return false;", &block)
-
end
-
-
1
def link_to_remove_asset(index, &block)
-
link_to_with_onclick_only("removeAsset(#{ index });return false;", &block)
-
end
-
-
1
def link_to_add_option(index, controller, model, &block)
-
link_to_with_onclick_only("addOption(#{ index },'#{ controller }','#{ model }');return false;", &block)
-
end
-
-
1
def link_to_remove_option(index, option, &block)
-
link_to_with_onclick_only("removeOption(#{ index },#{ option });return false;", &block);
-
end
-
-
1
private
-
-
1
def link_to_with_onclick_only(on_click_code, &block)
-
concat(
-
content_tag(
-
:a,
-
capture(&block),
-
:href => 'javascript:void();',
-
:onClick => on_click_code
-
)
-
)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module FieldInfosHelper
-
1
def field_info_id(path, field)
-
path = path.clone
-
return field if path.empty?
-
first = path.shift
-
to_bracketize = path+[field.key] #, "value"]
-
to_join = [first] + to_bracketize.map { |w| "[#{w}]" }
-
return to_join.join
-
end
-
-
1
def field_info_label(path, field)
-
(path + [field.key]).join('_')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
module LabInterface::WorkflowsHelper
-
-
# Returns descriptor from params, if it's not there try the @study.
-
# If @study's not set or it doesn't hold the descriptor, return a
-
# blank string...
-
1
def descriptor_value(descriptor)
-
# Refactored to remove reliance on @values
-
params[:values].try(:[], descriptor.name) or
-
@study.try(:descriptor_value,descriptor.name) or ""
-
end
-
-
# Returns a link to any available request comments with "None" as a
-
# default value.
-
1
def link_to_comments(request)
-
link_to_if(
-
request.comments.present?,
-
pluralize(request.comments.size, 'comment'),
-
request_comments_url(request)
-
) { "None" }
-
end
-
-
1
def shorten(string)
-
truncate string, 10, "..."
-
end
-
-
1
def not_so_shorten(string)
-
truncate string, 15, "..."
-
end
-
-
1
def tag_index_for(request)
-
batch_tag_index[request.asset_id]
-
end
-
-
1
def batch_tag_index
-
@tag_hash ||= Hash[Tag.find(:all,
-
:joins=>'INNER JOIN aliquots ON aliquots.tag_id = tags.id',
-
:select=>'tags.map_id, aliquots.receptacle_id AS receptacle_id',
-
:conditions=>['aliquots.receptacle_id IN (?)',@batch.requests.map(&:asset_id).uniq]).map do |tag|
-
[tag.receptacle_id,tag.map_id]
-
end].tap {|th| th.default = '-' }
-
end
-
-
1
def qc_select_box(request, status, html_options={})
-
select_options = [ 'pass', 'fail' ]
-
select_options.unshift('') if html_options.delete(:generate_blank)
-
select_tag("#{request.id}[qc_state]", options_for_select(select_options, status), html_options.merge(:class => 'qc_state'))
-
end
-
-
1
def gel_qc_select_box(request, status, html_options={})
-
blank = html_options.delete(:generate_blank) ? "<option></option>" : ""
-
if status.blank? || status == "Pass"
-
status = "OK"
-
end
-
select_tag("wells[#{request.id}][qc_state]", options_for_select({"Pass"=>"OK", "Fail"=>"Fail", "Weak"=>"Weak", "No Band"=>"Band Not Visible", "Degraded"=>"Degraded"}, status), html_options)
-
end
-
-
1
def request_types_sorted_by_total(workflow, project)
-
workflow.request_types.to_a.sort {|a,b| a.name <=> b.name}
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2013 Genome Research Ltd.
-
1
module PipelinesHelper
-
1
def next_pipeline_name_for(request)
-
submission = request.submission or return nil
-
first_next_request = submission.next_requests(request).first or return nil
-
first_next_request.request_type.name
-
end
-
-
1
def target_purpose_for(request)
-
nrs = request.submission.present? ? request.submission.next_requests(request) : []
-
return nrs.first.request_type.acceptable_plate_purposes.map(&:name).join('|') unless nrs.empty?
-
return request.target_purpose.try(:name) || 'Not specified'
-
end
-
-
1
def fluidigm_target?(batch)
-
batch.requests.where_is_a?(CherrypickForFluidigmRequest).present?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module PlatesHelper
-
-
1
class AliquotError < StandardError; end
-
-
1
def padded_wells_by_row(plate,overide =nil )
-
wells = wells_hash(plate)
-
padded_well_name_with_index(plate) do |padded_name,index|
-
index = padded_name == overide ? :overide : index
-
yield(padded_name,*wells[index])
-
end
-
end
-
-
1
private
-
-
1
def well_properties(well)
-
[
-
well.sample.name,
-
'',
-
well.sample.sample_metadata.sample_type||'Unknown'
-
]
-
end
-
-
1
def padded_well_name_with_index(plate)
-
('A'...(?A+(plate.size/12)).chr).each_with_index do |row,ri|
-
(1..12).each_with_index do |col,ci|
-
padded_name = "%s%02d" % [row, col]
-
index = ci+(ri*12)
-
yield(padded_name,index)
-
end
-
end
-
end
-
-
1
def wells_hash(plate)
-
Hash.new {|h,i| h[i] = ['[ Empty ]','', 'NTC'] }.tap do |wells|
-
wells[:overide] = ['','', 'NTC']
-
plate.wells.each do |well|
-
raise AliquotError if well.aliquots.count > 1
-
wells[well.map.row_order] = well_properties(well)
-
end
-
end
-
end
-
-
1
def self.event_family_for_pick(plate_purpose_name)
-
"picked_well_from_#{plate_purpose_name.gsub(/ /,"_").downcase}_plate"
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module PrototypeReplacementHelper
-
# def button_to_remote(*args); end
-
-
# button_to_remote "Remove", {:url => remove_user_role_admin_user_path(:id => @user, :role => {:authorizable_id => role.authorizable_id, :authorizable_type => role.authorizable_type.downcase, :authorizable_name => role.name }), :update => "role_list"}, {:class=>'btn btn-danger'}
-
# <%= form_tag [:grant_user_role_admin,@user], :remote => true, :data => {:success => "#role_list"}, :class => 'form-inline remote-form' do -%>
-
1
def remote_button(label,url,data,html_options={})
-
form_tag url, :remote => true, :data => data, :class => 'remote-form' do
-
submit_tag(label,html_options)
-
end
-
end
-
-
-
1
def tooltip(name='Help', opts={}, &block)
-
button = content_tag(:span,name,:class=>'btn btn-info popover-trigger',:'data-content' => capture(opts,&block),
-
:'data-toggle' => 'popover', :'data-title'=> opts.fetch(:title,'About this'))
-
concat button
-
end
-
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module RequestTypesHelper
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module RequestsHelper #:nodoc: all
-
-
1
def request_status(request)
-
state = request.state.blank? ? 'unknown' : request.state
-
content_tag(:span,state.upcase,:class=>"request-state text-#{bootstrapify(state.downcase)}")
-
end
-
-
1
def read_length(request)
-
if request.descriptor_value_for_key("read_length")
-
"(#{request.descriptor_value_for_key("read_length").value} cycles)"
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module SamplesHelper
-
# Use this wherever you are editing a sample so that you get the sample 'common name' lookup
-
# behaviour. Attach 'data-organism' attribute to the 'common name' and 'taxon ID' fields
-
# to get them updated.
-
1
def organism_validation_javascript
-
javascript_include_tag('organism_validation')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module Sdb::SampleManifestsHelper
-
-
1
def count_labels
-
{
-
'1dtube' => 'Tubes required',
-
'plate' => 'Plates required',
-
'multiplexed_library' => 'Number of samples in library'
-
}
-
end
-
-
1
def count_label_for(asset_type)
-
count_labels.fetch(params[:type],'Count')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module SequenomHelper
-
1
def dropdown_for_steps(field_name)
-
select_tag(field_name, options_for_select(SequenomController::STEPS.map(&:name), :select => SequenomController::STEPS.first.name))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module StudiesHelper
-
1
def status_link_title
-
if @study.inactive? || @study.pending?
-
"Open"
-
else
-
"Close"
-
end
-
end
-
-
1
def display_owner(study)
-
owners_for_display([ study.owner ].compact)
-
end
-
-
1
def display_owners(study)
-
owners = study.roles.map { |role| role.name == 'owner' ? role.users : nil }.compact
-
owners_for_display(owners.flatten)
-
end
-
-
1
private
-
-
1
def owners_for_display(owners)
-
owners.empty? ? 'Not available' : owners.map(&:name).join(', ')
-
end
-
-
1
public
-
-
1
def display_file_icon(document)
-
return image_tag("error.png") unless document
-
case document.content_type
-
when /pdf/
-
image_tag("pdf_icon.png", :size => "18x18")
-
when /word/
-
image_tag("word_icon.png")
-
when /excel/
-
image_tag("excel_icon.png")
-
else
-
image_tag("plaintext_icon.png")
-
end
-
end
-
-
1
def label_asset_state(asset)
-
asset.closed? ? "closed" : "open"
-
end
-
-
1
def study_link(study,options)
-
link_text = content_tag(:strong,study.name) << ' ' <<
-
content_tag(:span,study.state,:class=>"study-state label label-#{bootstrapify_study_state(study.state)}")
-
link_to(link_text, study_path(study), options)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Submission::WorkflowsHelper
-
1
def dynamic_link_to(summary_item)
-
object = summary_item.object
-
if object.instance_of?(Submission)
-
return link_to("Submission #{object.id}", study_workflow_submission_path(object.study, object.workflow, object))
-
elsif object.instance_of?(Asset)
-
return link_to("#{object.label.capitalize} #{object.name}", asset_path(object))
-
elsif object.instance_of?(Request)
-
return link_to("Request #{object.id}", request_path(object))
-
else
-
return 'No link available'
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
module SubmissionsHelper
-
-
# Returns an array (or anything else) as an escaped string for
-
# embedding in javascript
-
1
def stringify_array(projects_array)
-
projects_array.inspect
-
end
-
-
#<label for="submission_order_params_field_info_key">field_info.display_name/label>
-
1
def order_input_label(field_info)
-
label('submission[order_params]',field_info.key, field_info.display_name,:class=>'control-label col-sm-6')
-
end
-
-
# Returns a either a text input or a selection tag based on the 'kind'
-
# of the order parameter passed in.
-
# field_info is expected to be FieldInfo [sic]
-
1
def order_input_tag(order, field_info)
-
content_tag(:div,:class=>'col-sm-6') do
-
case field_info.kind
-
when "Selection" then order_selection_tag(order, field_info)
-
when "Text" then order_text_tag(order, field_info)
-
when "Numeric" then order_number_tag(order, field_info)
-
# Fall back to a text field
-
else order_text_tag(order, field_info)
-
end
-
end
-
end
-
-
1
def order_selection_tag(order, field_info)
-
select_tag(
-
"submission[order_params][#{field_info.key}]",
-
options_for_select(
-
field_info.selection.map(&:to_s),
-
order.request_options.try(:[], field_info.key)
-
),
-
:class => "required form-control",
-
:required => true,
-
:disabled => (field_info.selection.size == 1)
-
)
-
end
-
1
private :order_selection_tag
-
-
1
def order_text_tag(order, field_info)
-
text_field_tag(
-
"submission[order_params][#{field_info.key}]",
-
order.request_options.try(:[], field_info.key) || field_info.default_value,
-
:class => "required form-control",
-
:required => true
-
)
-
end
-
1
private :order_text_tag
-
-
1
def order_number_tag(order, field_info)
-
number_field_tag(
-
"submission[order_params][#{field_info.key}]",
-
order.request_options.try(:[], field_info.key) || field_info.default_value,
-
:class => "required form-control",
-
:required => true
-
)
-
end
-
1
private :order_text_tag
-
-
-
1
def studies_select(form, studies)
-
prompt = case studies.count
-
when 0 then "You are not managing any Studies at this time"
-
else "Please select a Study for this Submission..."
-
end
-
-
form.collection_select(
-
:study_id,
-
studies, :id, :name,
-
{ :prompt => prompt },
-
{ :disabled => true, :class => 'study_id form-control' }
-
)
-
end
-
-
1
def projects_select(form, projects)
-
prompt = case projects.count
-
when 0 then "There are no valid projects available"
-
else "Please select a Project for this Submission..."
-
end
-
# form.text_field :project_name,
-
# :class => 'submission_project_name form-control form-control',
-
# :placeholder => "enter the first few characters of the financial project name",
-
# :disabled => true
-
-
form.collection_select(
-
:project_name,
-
projects, :name, :name,
-
{ :prompt => prompt },
-
{ :disabled => true, :class => 'submission_project_name form-control' }
-
)
-
end
-
-
1
def asset_group_select(asset_groups)
-
prompt = case asset_groups.size
-
when 0 then "There are no Asset Groups associcated with this Study"
-
else 'Please select an asset group for this order.'
-
end
-
-
collection_select(
-
:submission,
-
:asset_group_id,
-
asset_groups, :id, :name,
-
{ :prompt => prompt },
-
{
-
:class => 'submission_asset_group_id required form-control',
-
:disabled => (asset_groups.size == 0)
-
}
-
)
-
end
-
-
1
def submission_status_message(submission)
-
case submission.state
-
when 'building' then
-
display_user_guide(
-
'This submission is still open for editing, further orders can still be added...',
-
edit_submission_path(submission)
-
) + button_to("Edit Submission", edit_submission_path(submission), :method => :get, :class => 'button')
-
when 'pending' then
-
display_user_guide( "Your submission is currently pending.") +
-
content_tag(:p, 'It should be processed approximately 10 minutes after you have submitted it, however sometimes this may take longer.')
-
when 'processing' then
-
display_user_guide("Your submission is currently being processed. This should take no longer than five minutes.")
-
when 'failed' then
-
display_user_error(raw("<h3>Your submission has failed:</h3><p> #{h((submission.message||'No failure reason recorded').lines.first)} </p>"))
-
when 'ready'
-
alert(:success) { raw('Your submission has been <strong>processed</strong>.') }
-
when 'cancelled'
-
alert(:info) { raw('Your submission has been <strong>cancelled</strong>.') }
-
else
-
alert(:danger) { 'Your submission is in an unknown state (contact support).' }
-
end
-
end
-
-
1
def order_sample_names(order)
-
order.assets.map(&:aliquots).flatten.map(&:sample).map(&:name).join(', ')
-
end
-
-
1
def request_description(presenter, request_type)
-
request_type_name = request_type.name.titleize
-
-
return request_type_name unless request_type.request_class_name =~ /SequencingRequest$/
-
-
content_tag(:em, pluralize(presenter.lanes_of_sequencing, 'Lane') + ' of ') + request_type_name
-
end
-
-
1
def submission_link(submission,options)
-
link_text = content_tag(:strong,submission.name) << ' ' <<
-
content_tag(:span,submission.state,:class=>"batch-state label label-#{bootstrapify_submission_state(submission.state)}")
-
link_to(link_text, submission_path(submission), options)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module UsersHelper
-
1
def logged_in_user?
-
yield if @user == current_user
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015 Genome Research Ltd.
-
# require './app/api/core/service'
-
1
module Api
-
1
class EndpointHandler < ::Core::Service
-
1
class << self
-
-
1
def registered_mimetypes
-
@registered_mimetypes||[]
-
end
-
-
# We can't use the built in provides, as the accepted mimetimes are fixed when the route is set up.
-
1
def file_requested(bool)
-
20
condition do
-
request.acceptable_media_types.prioritize(registered_mimetypes).present? == bool
-
end
-
end
-
-
1
def file_attatched(bool)
-
14
condition do
-
registered_mimetypes.include?(request.content_type) == bool
-
end
-
end
-
-
1
def file_addition(action, http_method)
-
2
send(http_method, %r{^/([\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})(?:/([^/]+(?:/[^/]+)*))?$}, :file_attatched=> true) do
-
raise Core::Service::ContentFiltering::InvalidRequestedContentTypeOnFile if request.acceptable_media_types.prioritize(registered_mimetypes).present?
-
report("file") do
-
filename = /filename="([^"]*)"/.match(request.env["HTTP_CONTENT_DISPOSITION"]).try(:[],1)||"unnamed_file"
-
begin
-
-
file = Tempfile.new(filename)
-
file.binmode
-
file.unlink
-
file.write(request.body.read)
-
# Be kind...
-
file.rewind
-
request.body.rewind
-
uuid_in_url, parts = params[:captures][0], params[:captures][1].try(:split, '/')|| []
-
uuid = Uuid.with_external_id(uuid_in_url).first or raise ActiveRecord::RecordNotFound, "UUID does not exist"
-
handle_request(:instance, request, action, parts) do |request|
-
request.io = lookup_for_class(uuid.resource.class) { |e| raise e }
-
request.target = request.io.eager_loading_for(uuid.resource.class).include_uuid.find(uuid.resource_id)
-
request.file = file
-
request.filename = filename
-
end
-
ensure
-
file.close!
-
end
-
end
-
end
-
end
-
-
1
def file_model_addition(action, http_method)
-
2
send(http_method, %r{^/([^\d/][^/]+(?:/[^/]+){0,2})$}, :file_attatched=> true) do
-
raise Core::Service::ContentFiltering::InvalidRequestedContentType if request.acceptable_media_types.prioritize(registered_mimetypes).present?
-
report("model") do
-
filename = /filename="([^"]*)"/.match(request.env["HTTP_CONTENT_DISPOSITION"]).try(:[],1)||"unnamed_file"
-
begin
-
file = Tempfile.new(filename)
-
file.write(request.body.read)
-
# Be kind...
-
file.rewind
-
request.body.rewind
-
determine_model_from_parts(*params[:captures].join.split('/')) do |model, parts|
-
handle_request(:model, request, action, parts) do |request|
-
request.io = lookup_for_class(model) { |_| nil }
-
request.target = model
-
request.file = file
-
request.filename = filename
-
end
-
end
-
ensure
-
file.close!
-
end
-
end
-
end
-
end
-
-
1
def file_model_action(action, http_method)
-
4
send(http_method, %r{^/([^\d/][^/]+(?:/[^/]+){0,2})$}, :file_requested=>true) do
-
report("model") do
-
raise Core::Service::ContentFiltering::InvalidRequestedContentType
-
end
-
end
-
end
-
-
1
def file_action(action, http_method)
-
4
send(http_method, %r{^/([\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})(?:/([^/]+(?:/[^/]+)*))?$}, :file_requested=>true) do
-
report("file") do
-
uuid_in_url, parts = params[:captures][0], params[:captures][1].try(:split, '/') || []
-
uuid = Uuid.with_external_id(uuid_in_url).first or raise ActiveRecord::RecordNotFound, "UUID does not exist"
-
-
file_through = return_file(request, action, parts) do |request|
-
request.io = lookup_for_class(uuid.resource.class) { |e| raise e }
-
request.target = request.io.eager_loading_for(uuid.resource.class).include_uuid.find(uuid.resource_id)
-
end
-
uuid.resource.__send__(file_through) {|file| send_file file.path, :filename=> file.filename }
-
-
end
-
end
-
end
-
-
1
def instance_action(action, http_method)
-
4
send(http_method, %r{^/([\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})(?:/([^/]+(?:/[^/]+)*))?$}, :file_attatched=> false, :file_requested=>false) do
-
report("instance") do
-
uuid_in_url, parts = params[:captures][0], params[:captures][1].try(:split, '/') || []
-
uuid = Uuid.with_external_id(uuid_in_url).first or raise ActiveRecord::RecordNotFound, "UUID does not exist"
-
handle_request(:instance, request, action, parts) do |request|
-
request.io = lookup_for_class(uuid.resource.class) { |e| raise e }
-
request.target = request.io.eager_loading_for(uuid.resource.class).include_uuid.find(uuid.resource_id)
-
end
-
end
-
end
-
end
-
-
1
def model_action(action, http_method)
-
4
send(http_method, %r{^/([^\d/][^/]+(?:/[^/]+){0,2})$}, :file_attatched=> false, :file_requested=>false) do
-
report("model") do
-
determine_model_from_parts(*params[:captures].join.split('/')) do |model, parts|
-
handle_request(:model, request, action, parts) do |request|
-
request.io = lookup_for_class(model) { |_| nil }
-
request.target = model
-
end
-
end
-
end
-
end
-
end
-
-
-
1
def register_mimetype(mimetype)
-
1
@registered_mimetypes ||= []
-
1
@registered_mimetypes.push(mimetype).uniq!
-
end
-
-
end
-
-
1
def registered_mimetypes
-
self.class.registered_mimetypes
-
end
-
-
-
1
def lookup_for_class(model, &block)
-
::Core::Io::Registry.instance.lookup_for_class(model)
-
rescue ::Core::Registry::UnregisteredError => exception
-
block.call(exception)
-
end
-
1
private :lookup_for_class
-
-
# Report the performance and status of any request
-
1
def report(handler, &block)
-
start = Time.now
-
Rails.logger.info("API[start]: #{handler}: #{request.fullpath}")
-
yield
-
ensure
-
Rails.logger.info("API[handled]: #{handler}: #{request.fullpath} in #{Time.now-start}s")
-
end
-
1
private :report
-
-
# Not ideal but at least this allows us to pick up the appropriate model from the URL.
-
1
def determine_model_from_parts(*parts)
-
(1..parts.length).to_a.reverse.each do |n|
-
begin
-
model_name, remainder = parts.slice(0, n), parts.slice(n, parts.length)
-
return yield(model_name.join('/').classify.constantize, remainder)
-
rescue NameError => exception
-
# Re-raise No Method Errors as it means something is wrong
-
raise exception if exception.is_a?(NoMethodError)
-
# Nope, try again
-
end
-
end
-
raise StandardError, "Cannot route #{parts.join('/').inspect}"
-
end
-
1
private :determine_model_from_parts
-
-
1
def handle_request(handler, http_request, action, parts)
-
endpoint_lookup, io_lookup =
-
case handler
-
when :instance then [ :endpoint_for_object, :lookup_for_object ]
-
when :model then [ :endpoint_for_class, :lookup_for_class ]
-
else raise StandardError, "Unexpected handler #{handler.inspect}"
-
end
-
-
request =
-
::Core::Service::Request.new(requested_url = http_request.fullpath) do |request|
-
request.service = self
-
request.path = parts
-
request.json = @json
-
yield(request)
-
end
-
-
endpoint = send(endpoint_lookup, request.target)
-
Rails.logger.info("API[endpoint]: #{handler}: #{requested_url} handled by #{endpoint.inspect}")
-
body(request.send(handler, action, endpoint))
-
end
-
-
1
def return_file(http_request, action, parts)
-
-
request =
-
::Core::Service::Request.new(requested_url = http_request.fullpath) do |request|
-
request.service = self
-
request.path = parts
-
request.json = @json
-
yield(request)
-
end
-
-
endpoint = endpoint_for_object(request.target)
-
file_through = request.instance(action, endpoint).handled_by.file_through(request_accepted)
-
raise Core::Service::ContentFiltering::InvalidRequestedContentType if file_through.nil?
-
Rails.logger.info("API[endpoint]: File: #{requested_url} handled by #{endpoint.inspect}")
-
file_through
-
end
-
-
1
ACTIONS_TO_HTTP_VERBS = {
-
:create => :post,
-
:read => :get,
-
:update => :put,
-
:delete => :delete
-
}
-
-
1
ACTIONS_TO_HTTP_VERBS.each do |action, verb|
-
4
instance_action(action, verb)
-
4
model_action(action, verb)
-
4
file_action(action,verb)
-
4
file_model_action(action,verb)
-
end
-
-
{
-
1
:create_from_file => :post,
-
:update_from_file => :put
-
}.each do |action, verb|
-
2
file_addition(action,verb)
-
2
file_model_addition(action,verb)
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
# require './app/api/core/service'
-
1
module Api
-
1
class RootService < ::Core::Service
-
# NOTE: This is partly a hack but it suffices to keep the dynamic ability to write endpoints.
-
1
ALL_SERVICES_AVAILABLE = Hash[Dir.glob(File.join(Rails.root, %w{app api endpoints ** *.rb})).map do |file|
-
60
handler = file.gsub(%r{^.+/(endpoints/.+).rb$}, '\1').camelize.constantize
-
60
[ handler.root.gsub('/', '_'), handler ]
-
end]
-
-
1
use Api::EndpointHandler
-
-
1
module RootResponse
-
1
def services(services)
-
self.object = services
-
def @owner.each(&block)
-
::Core::Io::Buffer.new(block) do |buffer|
-
::Core::Io::Json::Stream.new(buffer).open do |stream|
-
stream.attribute('revision', 2)
-
object.each do |model_in_json, endpoint|
-
stream.block(model_in_json) do |nested_stream|
-
nested_stream.block('actions') do |actions_stream|
-
endpoint.model_handler.send(
-
:actions,
-
endpoint.model_handler,
-
:response => self, :endpoint => endpoint
-
).map do |action,url|
-
actions_stream.attribute(action,url)
-
end
-
end
-
end
-
end
-
end
-
end
-
# json = Hash[
-
# object.map do |model_in_json,endpoint|
-
# [model_in_json, endpoint.model_handler.as_json(:response => self, :endpoint => endpoint, :target => endpoint.model_handler)]
-
# end +
-
# [ [ 'revision', 2 ] ]
-
# ]
-
# #Yajl::Encoder.new.encode(json, &block)
-
# yield JSON.generate(json)
-
end
-
end
-
end
-
-
# It appears that if you go through a service like nginx or mongrel cluster(?) that the trailing
-
# slash gets stripped off any requests, so we have to account for that with the root actions.
-
1
get(%r{^/?$}) do
-
result = report("root") do
-
::Core::Service::Request.new(request.fullpath) do |request|
-
request.service = self
-
request.path = '/'
-
end.response do |response|
-
class << response ; include RootResponse ; end
-
response.services(ALL_SERVICES_AVAILABLE)
-
end
-
end
-
-
body(result)
-
end
-
-
1
[ :post, :put, :delete ].each do |action|
-
3
send(action, %r{^/?$}) do
-
raise MethodNotAllowed, [ :get ]
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class AccessionService
-
1
AccessionServiceError = Class.new(StandardError)
-
1
NumberNotRequired = Class.new(AccessionServiceError)
-
1
NumberNotGenerated = Class.new(AccessionServiceError)
-
-
1
CenterName = 'SC'.freeze # TODO [xxx] use confing file
-
1
Protect = "protect".freeze
-
1
Hold = "hold".freeze
-
-
1
def provider; end
-
-
1
class AccessionedFile < File
-
# This class provides an original_filename method
-
# which RestClient can use to define the remote filename
-
1
attr_accessor :original_filename
-
end
-
-
1
def submit(user, *accessionables)
-
ActiveRecord::Base.transaction do
-
submission = Accessionable::Submission.new(self, user, *accessionables)
-
-
errors = submission.all_accessionables.map(&:errors).flatten
-
-
raise AccessionServiceError, errors.join("\n") unless errors.empty?
-
-
files = [] # maybe not necessary, but just to be sure that the tempfile still exists when they are sent
-
begin
-
xml_result = post_files(submission.all_accessionables.map do |acc|
-
file = Tempfile.open("#{acc.schema_type}_file")
-
files << file
-
file.puts(acc.xml)
-
file.open # reopen for read
-
-
Rails::logger.debug { file.each_line.to_a.join("\n") }
-
-
{:name => acc.schema_type.upcase, :local_name => file.path, :remote_name => acc.file_name }
-
end
-
)
-
-
Rails::logger.debug { xml_result }
-
raise AccessionServiceError, "EBI Server Error. Couldnt get accession number: #{xml_result}" if xml_result =~ /(Server error|Auth required|Login failed)/
-
-
xmldoc = Document.new(xml_result)
-
success = xmldoc.root.attributes['success']
-
accession_numbers = []
-
# for some reasons, ebi doesn't give us back a accession number for the submission if it's a MODIFY action
-
# therefore, we should be ready to get one or not
-
number_generated = true
-
if success == 'true'
-
#extract and update accession numbers
-
accession_number = submission.all_accessionables.each do |acc|
-
accession_number = acc.extract_accession_number(xmldoc)
-
if accession_number
-
acc.update_accession_number!(user, accession_number)
-
accession_numbers << accession_number
-
else
-
# error only, if one of the expected accessionable didn't get a AN
-
# We don't care about the submission
-
number_generated = false if accessionables.include?(acc)
-
end
-
ae_an = acc.extract_array_express_accession_number(xmldoc)
-
acc.update_array_express_accession_number!(ae_an) if ae_an
-
end
-
-
raise NumberNotGenerated, 'Service gave no numbers back' unless number_generated
-
-
elsif success == 'false'
-
errors = xmldoc.root.elements.to_a("//ERROR").map(&:text)
-
raise AccessionServiceError, "Could not get accession number. Error in submitted data: #{$!} #{ errors.map { |e| "\n - #{e}"} }"
-
else
-
raise AccessionServiceError, "Could not get accession number. Error in submitted data: #{$!}"
-
end
-
ensure
-
files.each { |f| f.close } # not really necessary but recommended
-
end
-
-
return accessionables.map(&:accession_number)
-
end
-
end
-
-
1
def submit_sample_for_user(sample, user)
-
raise NumberNotRequired, 'Does not require an accession number' unless sample.studies.first.ena_accession_required?
-
-
ebi_accession_number = sample.sample_metadata.sample_ebi_accession_number
-
#raise NumberNotGenerated, 'No need to' if not ebi_accession_number.blank? and not /ERS/.match(ebi_accession_number)
-
-
submit(user, Accessionable::Sample.new(sample))
-
end
-
-
1
def submit_study_for_user(study, user)
-
raise NumberNotRequired, 'An accession number is not required for this study' unless study.ena_accession_required?
-
-
#TODO check error
-
#raise AccessionServiceError, "Cannot generate accession number: #{ sampledata[:error] }" if sampledata[:error]
-
-
-
ebi_accession_number = study.study_metadata.study_ebi_accession_number
-
#raise NumberNotGenerated, 'No need to' if not ebi_accession_number.blank? and not /ER/.match(ebi_accession_number)
-
-
return submit(user, Accessionable::Study.new(study))
-
end
-
-
1
def submit_dac_for_user(study, user)
-
raise NumberNotRequired, "No need to"
-
end
-
-
1
def accession_study_xml(study)
-
Accessionable::Study.new(study).xml
-
end
-
-
1
def accession_sample_xml(sample)
-
Accessionable::Sample.new(sample).xml
-
end
-
-
1
def accession_policy_xml(study)
-
policy = Accessionable::Policy.new(study)
-
policy.xml
-
end
-
-
1
def accession_dac_xml(study)
-
Accessionable::Dac.new(study).xml
-
end
-
-
1
def sample_visibility(sample)
-
Protect
-
end
-
-
1
def study_visibility(study)
-
Protect
-
end
-
-
1
def policy_visibility(study)
-
Protect
-
end
-
-
1
def dac_visibility(study)
-
Protect
-
end
-
-
1
def private?
-
false
-
end
-
-
1
private
-
-
1
def accession_study_set_xml_quarantine(study, studydata)
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.STUDY_SET('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') {
-
xml.STUDY(:alias => studydata[:alias], :accession => study.study_metadata.study_ebi_accession_number) {
-
xml.DESCRIPTOR {
-
xml.STUDY_TITLE studydata[:study_title]
-
xml.STUDY_DESCRIPTION studydata[:description]
-
xml.CENTER_PROJECT_NAME studydata[:center_study_name]
-
xml.CENTER_NAME studydata[:center_name]
-
xml.STUDY_ABSTRACT studydata[:study_abstract]
-
-
xml.PROJECT_ID(studydata[:study_id] || "0")
-
study_type = studydata[:existing_study_type]
-
if StudyType.include?(study_type)
-
xml.STUDY_TYPE(:existing_study_type => study_type)
-
else
-
xml.STUDY_TYPE(:existing_study_type => Study::Other_type , :new_study_type => study_type)
-
end
-
}
-
}
-
}
-
return xml.target!
-
end
-
-
1
def accession_sample_set_xml_quarantine(sample, sampledata)
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.SAMPLE_SET('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') {
-
xml.SAMPLE(:alias => sampledata[:alias], :accession => sample.sample_metadata.sample_ebi_accession_number) {
-
xml.SAMPLE_NAME {
-
xml.COMMON_NAME sampledata[:sample_common_name]
-
xml.TAXON_ID sampledata[:taxon_id]
-
}
-
xml.SAMPLE_ATTRIBUTES {
-
sampledata[:tags].each do |tagpair|
-
xml.SAMPLE_ATTRIBUTE {
-
xml.TAG tagpair[:tag]
-
xml.VALUE tagpair[:value]
-
}
-
end
-
} unless sampledata[:tags].blank?
-
-
xml.SAMPLE_LINKS {
-
-
} unless sampledata[:links].blank?
-
}
-
}
-
return xml.target!
-
end
-
-
1
def accession_submission_xml(submission, accession_number)
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.SUBMISSION(
-
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
-
:center_name => submission[:center_name],
-
:broker_name => submission[:broker],
-
:alias => submission[:submission_id],
-
:submission_date => submission[:submission_date]
-
) {
-
xml.CONTACTS {
-
xml.CONTACT(
-
:inform_on_error => submission[:contact_inform_on_error],
-
:inform_on_status => submission[:contact_inform_on_status],
-
:name => submission[:name]
-
)
-
}
-
xml.ACTIONS {
-
xml.ACTION {
-
if accession_number.blank?
-
xml.ADD(:source => submission[:source], :schema => submission[:schema])
-
else
-
xml.MODIFY(:source => submission[:source], :target=>"")
-
end
-
}
-
xml.ACTION {
-
if submission[:hold] == AccessionService::Protect
-
xml.PROTECT
-
else
-
xml.HOLD
-
end
-
}
-
}
-
}
-
return xml.target!
-
end
-
-
1
require 'rexml/document'
-
#require 'curb'
-
1
include REXML
-
-
1
def accession_login
-
raise NotImplemented, "abstract method"
-
end
-
-
1
def post_files(file_params)
-
raise StandardError, "Cannot connect to EBI to get accession number. Please configure accession_url in config.yml" if configatron.accession_url.blank?
-
-
begin
-
rc = RestClient::Resource.new(URI.parse(configatron.accession_url+accession_login).to_s)
-
if configatron.disable_web_proxy == true
-
RestClient.proxy = ''
-
elsif not configatron.proxy.blank?
-
RestClient.proxy = configatron.proxy
-
# UA required to get through Sanger proxy
-
# Although currently this UA is actually being set elsewhere in the
-
# code as RestClient doesn't pass this header to the proxy.
-
rc.options[:headers]={:user_agent=>"Sequencescape Accession Client (#{Rails.env})"}
-
end
-
-
payload = {}
-
file_params.map { |p|
-
payload[p[:name]] = AccessionedFile.open(p[:local_name]).tap{|f| f.original_filename = p[:remote_name] }
-
}
-
#rc.multipart_form_post = true # RC handles automatically
-
response = rc.post(payload)
-
case response.code
-
when (200...300) #success
-
return response.body.to_s
-
when (400...600)
-
Rails.logger.warn($!)
-
$! = nil
-
raise AccessionServiceError
-
else
-
return ""
-
end
-
rescue StandardError => exception
-
raise AccessionServiceError, "Could not get accession number. EBI may be down or invalid data submitted: #{$!}"
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Accessionable::Base
-
1
InvalidData = Class.new(AccessionService::AccessionServiceError)
-
1
attr_reader :accession_number, :name, :date, :date_short
-
1
def initialize(accession_number)
-
@accession_number = accession_number
-
-
time_now = Time.now
-
@date = time_now.strftime("%Y-%m-%dT%H:%M:%SZ")
-
@date_short = time_now.strftime("%Y-%m-%d")
-
-
end
-
-
1
def errors
-
[]
-
end
-
-
1
def xml
-
raise NotImplementedError, "abstract method"
-
end
-
-
1
def center_name
-
AccessionService::CenterName
-
end
-
-
1
def schema_type
-
#raise NotImplementedError, "abstract method"
-
self.class.name.split("::").last.downcase
-
end
-
-
1
def alias
-
"#{name.gsub(/[^a-z\d]/i, '_')}-sc-#{accessionable_id}"
-
end
-
-
1
def file_name
-
"#{self.alias}-#{date}.#{schema_type}.xml"
-
end
-
-
1
def extract_accession_number(xmldoc)
-
element = xmldoc.root.elements["/RECEIPT/#{schema_type.upcase}"]
-
accession_number = element && element.attributes['accession']
-
end
-
-
1
def extract_array_express_accession_number(xmldoc)
-
element = xmldoc.root.elements["/RECEIPT/#{schema_type.upcase}/EXT_ID[@type='ArrayExpress']"]
-
accession_number = element && element.attributes['accession']
-
end
-
-
1
def update_accession_number!(user, accession_number)
-
raise NotImplementedError, "abstract method"
-
end
-
-
1
def update_array_express_accession_number!(accession_number)
-
end
-
-
1
def accessionable_id
-
raise NotImplementError, "abstract method"
-
end
-
-
1
def released?
-
# Return false by default. Overidden by sample.
-
false
-
end
-
-
1
def add_updated_event(user, classname, eventable)
-
eventable.events.create(
-
:created_by => user.login,
-
:message => "#{classname} #{eventable.id} accession data has been updated by user #{user.login}",
-
:content => "accession number regenerated",
-
:of_interest_to => "administrators"
-
)
-
end
-
1
def label_scope
-
@label_scope ||= "metadata.#{self.class.name.split("::").last.downcase}.metadata"
-
end
-
-
1
class Tag
-
1
attr_reader :value
-
1
def initialize(label_scope, name, value, downcase = false)
-
@name = name
-
@value = downcase && value ? value.downcase : value
-
@scope = label_scope
-
end
-
-
1
def label
-
I18n.t("#{@scope}.#{ @name }.label").gsub(' ','_').downcase
-
end
-
-
1
def build(xml)
-
xml.TAG label
-
xml.VALUE value
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Accessionable::Dac < Accessionable::Base
-
-
1
attr_reader :contacts
-
1
def initialize(study)
-
@study = study
-
@name = study.dac_refname
-
@contacts = study.send("Data Access Contacts").map do |contact|
-
{
-
:email => contact.email,
-
:name => contact.name,
-
:organisation => AccessionService::CenterName
-
}
-
end
-
-
super(study.dac_accession_number)
-
end
-
-
1
def errors
-
[].tap do |errors|
-
if @contacts.empty?
-
errors << "Data Access Contacts Empty. Please add a contact"
-
end
-
end
-
end
-
-
1
def xml
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.DAC_SET('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') {
-
xml.DAC(:alias => self.alias, :accession => self.accession_number, :center_name => self.center_name) {
-
xml.CONTACTS {
-
self.contacts.each do |contact|
-
xml.CONTACT( {
-
:name => contact[:name],
-
:email => contact[:email],
-
:organisation => contact[:organisation]
-
}.tap do |att|
-
att[:telephone]=tel if (tel=contact[:telephone])
-
end)
-
end
-
}
-
}
-
}
-
return xml.target!
-
end
-
-
1
def update_accession_number!(user, accession_number)
-
@accession_number = accession_number
-
add_updated_event(user, "DAC for Study #{@study.id}", @study) if @accession_number
-
@study.study_metadata.ega_dac_accession_number = accession_number
-
@study.save!
-
end
-
-
1
def protect?(service)
-
service.dac_visibility(@study) == AccessionService::Protect
-
end
-
-
1
def accessionable_id
-
@study.id
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Accessionable::Policy < Accessionable::Base
-
-
1
attr_reader :policy_url, :dac_accession_number, :title
-
-
1
def initialize(study)
-
@study = study
-
-
@name = "Policy for study - #{study.name} - ##{study.id}"
-
@policy_url = study.study_metadata.dac_policy
-
@title = study.study_metadata.dac_policy_title
-
#@dac_refname = study.dac_refname
-
@dac_accession_number = study.dac_accession_number
-
super(study.policy_accession_number)
-
-
end
-
-
1
def errors
-
[].tap do |errors|
-
errors << "DAC Accession number not found. Please get an accession number for the DAC." unless @dac_accession_number
-
end
-
end
-
-
1
def xml
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.POLICY_SET('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') {
-
xml.POLICY(:alias => self.alias,
-
:accession => self.accession_number,
-
:center_name => self.center_name) {
-
xml.TITLE self.title
-
xml.DAC_REF(:accession => self.dac_accession_number)
-
xml.POLICY_FILE self.policy_url
-
}
-
}
-
return xml.target!
-
end
-
-
1
def update_accession_number!(user, accession_number)
-
@accession_number = accession_number
-
add_updated_event(user, "Policy for Study #{@study.id}", @study) if @accession_number
-
@study.study_metadata.ega_policy_accession_number = accession_number
-
@study.save!
-
end
-
-
1
def protect?(service)
-
service.policy_visibility(@study) == AccessionService::Protect
-
end
-
-
1
def accessionable_id
-
@study.id
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
module Accessionable
-
1
class Sample < Base
-
1
attr_reader :common_name, :taxon_id, :links, :tags
-
1
def initialize(sample)
-
@sample = sample
-
super(sample.ebi_accession_number)
-
-
sampname = sample.sample_metadata.sample_public_name
-
@name = sampname.blank? ? sample.name : sampname
-
@name = @name.gsub(/[^a-z\d]/i,'_') unless @name.blank?
-
-
@common_name = sample.sample_metadata.sample_common_name
-
@taxon_id = sample.sample_metadata.sample_taxon_id
-
-
# Tags from the 'ENA attributes' property group
-
# NOTE[xxx]: This used to also look for 'ENA links' and push them to the 'data[:links]' value, but group was empty
-
@links = []
-
@tags = sample.tags.map do |datum|
-
Tag.new(label_scope, datum.name, sample.sample_metadata[datum.tag], datum.downcase)
-
end
-
-
#TODO maybe unify this with the previous loop
-
# Don't send managed AE data to SRA
-
if !sample.accession_service.private?
-
::Sample::ArrayExpressFields.each do |datum|
-
value = sample.sample_metadata.send(datum)
-
next unless value.present?
-
@tags << ArrayExpressTag.new(label_scope, datum, value)
-
end
-
end
-
-
sample_hold = sample.sample_metadata.sample_sra_hold
-
@hold = sample_hold.blank? ? 'hold' : sample_hold
-
end
-
-
1
def accessionable_id
-
@sample.id
-
end
-
-
1
def alias
-
@sample.uuid
-
end
-
-
1
def title
-
@sample.sample_metadata.sample_public_name || @sample.sanger_sample_id
-
end
-
-
1
def sample_element_attributes
-
# In case the accession number is defined, we won't send the alias
-
{
-
:alias => self.alias,
-
:accession => accession_number
-
}.tap do |obj|
-
obj.delete(:alias) unless self.accession_number.blank?
-
end
-
end
-
-
1
def xml
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.SAMPLE_SET('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') {
-
xml.SAMPLE(sample_element_attributes) {
-
xml.TITLE self.title unless title.nil?
-
xml.SAMPLE_NAME {
-
xml.COMMON_NAME self.common_name
-
xml.TAXON_ID self.taxon_id
-
}
-
xml.SAMPLE_ATTRIBUTES {
-
self.tags.each do |tag|
-
xml.SAMPLE_ATTRIBUTE {
-
tag.build(xml)
-
}
-
end
-
} unless self.tags.blank?
-
-
xml.SAMPLE_LINKS {
-
-
} unless self.links.blank?
-
}
-
}
-
return xml.target!
-
end
-
-
1
def update_accession_number!(user, accession_number)
-
@accession_number = accession_number
-
add_updated_event(user, "Sample #{@sample.id}", @sample) if @accession_number
-
@sample.sample_metadata.sample_ebi_accession_number = accession_number
-
@sample.save!
-
end
-
-
1
def protect?(service)
-
service.sample_visibility(@sample) == AccessionService::Protect
-
end
-
-
1
def released?
-
@sample.released?
-
end
-
-
end
-
1
private
-
-
1
class ArrayExpressTag < Base::Tag
-
1
def label
-
default_tag = "ArrayExpress-#{I18n.t("#{@scope}.#{ @name }.label").gsub(" ","_").camelize}"
-
I18n.t("#{@scope}.#{ @name }.era_label", :default => default_tag)
-
end
-
end
-
-
1
class EgaTag< Base::Tag
-
1
def label
-
default_tag = "EGA-#{I18n.t("#{@scope}.#{ @name }.label").gsub(" ","_").camelize}"
-
I18n.t("#{@scope}.#{ @name }.era_label", :default => default_tag)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Accessionable
-
1
class Study < Base
-
1
attr_reader :study_title, :description, :center_study_name, :study_abstract, :existing_study_type, :tags, :related_studies
-
1
def initialize(study)
-
@study = study
-
data = {}
-
-
study_title = study.study_metadata.study_study_title
-
@name = study_title.blank? ? '' : study_title.gsub(/[^a-z\d]/i, '_')
-
-
study_type = study.study_metadata.study_type.name
-
@existing_study_type = study_type # the study type if validated is exactly the one submission need
-
-
@study_title = @name
-
@center_study_name = @study_title
-
-
pid = study.study_metadata.study_project_id
-
@study_id = pid.blank? ? '0' : pid
-
-
study_abstract = study.study_metadata.study_abstract
-
@study_abstract = study_abstract unless study_abstract.blank?
-
-
study_desc = study.study_metadata.study_description
-
@description = study_desc unless study_desc.blank?
-
-
@tags = []
-
@tags << Tag.new(self.label_scope, "ArrayExpress", nil) if study.for_array_express?
-
super(study.study_metadata.study_ebi_accession_number)
-
-
@related_studies = []
-
study.study_relations.each do |r|
-
@related_studies << RelatedStudy.new(r.related_study, r.name)
-
end
-
study.reversed_study_relations.each do |r|
-
rs=RelatedStudy.new(r.study, r.reversed_name)
-
@related_studies << rs if rs.to_send?
-
end
-
end
-
-
1
def errors
-
error_list = []
-
error_list + @related_studies.map(&:errors)
-
end
-
-
1
def xml
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.STUDY_SET('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance') {
-
xml.STUDY(:alias => self.alias, :accession => self.accession_number) {
-
xml.DESCRIPTOR {
-
xml.STUDY_TITLE self.study_title
-
xml.STUDY_DESCRIPTION self.description
-
xml.CENTER_PROJECT_NAME self.center_study_name
-
xml.CENTER_NAME self.center_name
-
xml.STUDY_ABSTRACT self.study_abstract
-
-
xml.PROJECT_ID(self.accessionable_id || "0")
-
study_type = self.existing_study_type
-
if StudyType.include?(study_type)
-
xml.STUDY_TYPE(:existing_study_type => study_type)
-
else
-
xml.STUDY_TYPE(:existing_study_type => ::Study::Other_type , :new_study_type => study_type)
-
end
-
-
xml.RELATED_STUDIES {
-
self.related_studies.each do |study|
-
study.build(xml)
-
end
-
} unless self.related_studies.blank?
-
}
-
xml.STUDY_ATTRIBUTES {
-
self.tags.each do |tag|
-
xml.STUDY_ATTRIBUTE {
-
tag.build(xml)
-
}
-
end
-
} unless self.tags.blank?
-
}
-
}
-
return xml.target!
-
end
-
-
1
def accessionable_id
-
@study.id
-
end
-
-
1
def protect?(service)
-
service.study_visibility(@study) == AccessionService::Protect
-
end
-
-
1
def update_accession_number!(user, accession_number)
-
@accession_number = accession_number
-
add_updated_event(user, "Study #{@study.id}", @study) if @accession_number
-
@study.study_metadata.study_ebi_accession_number = accession_number
-
@study.save!
-
end
-
-
1
def update_array_express_accession_number!(number)
-
@study.study_metadata.array_express_accession_number = number
-
@study.save!
-
end
-
-
end
-
1
private
-
1
class RelatedStudy
-
1
def initialize(study, role, primary = false)
-
@study = study
-
@role = role
-
@primary = primary
-
end
-
-
# return if the Link would need to be send to the accession service
-
1
def to_send?
-
db_label.present?
-
end
-
-
1
def errors
-
[].tap do |errs|
-
errs << "Accession number needed for related study #{@study.name}" if @study.ebi_accession_number.blank?
-
end
-
end
-
-
1
def build(xml)
-
return if db_label.blank?
-
xml.RELATED_STUDY {
-
xml.RELATED_LINK {
-
xml.DB db_label
-
xml.ID @study.ebi_accession_number
-
}
-
xml.IS_PRIMARY @primary
-
}
-
end
-
-
1
def db_label
-
I18n.t("metadata.study.metadata.#{ @role }.ebi_db", :default => "")
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Accessionable::Submission < Accessionable::Base
-
1
attr_reader :broker, :alias, :date, :accessionables, :contact
-
-
1
def initialize(service, user, *accessionables)
-
@service = service
-
@contact = Contact.new(user)
-
@broker = service.broker
-
@accessionables = accessionables
-
-
super(accession_number)
-
end
-
-
1
def alias
-
@accessionables.map(&:alias).join(" - ") << DateTime.now.strftime('%Y%m%dT%H%M')
-
end
-
-
1
def <<(accessionable)
-
@accessionables << accesionable
-
end
-
-
1
def xml
-
xml = Builder::XmlMarkup.new
-
xml.instruct!
-
xml.SUBMISSION(
-
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
-
:center_name => self.center_name,
-
:broker_name => self.broker,
-
:alias => self.alias,
-
:submission_date => self.date
-
) {
-
xml.CONTACTS { self.contact.build(xml) }
-
xml.ACTIONS {
-
# You can only perform additions with protect or hold, or you can do a modification. So separate the
-
# accessionable instances into additions and modifications.
-
additions, modifications = accessionables.partition { |accessionable| accessionable.accession_number.blank? }
-
-
additions.each do |accessionable|
-
xml.ACTION {
-
xml.ADD(:source => accessionable.file_name, :schema => accessionable.schema_type)
-
}
-
-
xml.ACTION {
-
xml.tag!(accessionable.protect?(@service) ? 'PROTECT' : 'HOLD')
-
}
-
end
-
-
-
modifications.each do |accessionable|
-
xml.ACTION {
-
xml.MODIFY(
-
:source => accessionable.file_name,
-
:schema => accessionable.schema_type
-
)
-
}
-
-
state_action(accessionable) do |action|
-
xml.ACTION {
-
xml.tag!(action)
-
}
-
end
-
end
-
}
-
}
-
return xml.target!
-
end
-
-
-
1
def state_action(accessionable)
-
if accessionable.protect?(@service)
-
yield 'PROTECT'
-
elsif !accessionable.released?
-
yield 'HOLD'
-
end
-
end
-
-
1
def name
-
if @accessionables.size >= 1
-
@accessionables.first.name
-
else
-
"empty"
-
end
-
end
-
-
1
def all_accessionables
-
@accessionables + [self]
-
end
-
-
1
def update_accession_number!(user, accession_number)
-
@accession_number = accession_number
-
end
-
-
1
private
-
-
1
class Contact
-
1
attr_reader :inform_on_error, :inform_on_status, :name
-
1
def initialize(user)
-
@inform_on_error = "#{user.login}@#{configatron.default_email_domain}"
-
@inform_on_status = inform_on_error
-
@name = user.first_name+" "+user.last_name
-
end
-
-
1
def build(markup)
-
markup.CONTACT(
-
:inform_on_error => inform_on_error,
-
:inform_on_status => inform_on_status,
-
:name => name
-
)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class AddSpikedInControlTask < Task
-
-
1
def partial
-
"add_spiked_in_control"
-
end
-
1
def do_task(controller, params)
-
controller.do_add_spiked_in_control_task(self, params)
-
end
-
-
1
def add_control(batch, control_asset, request_id_set)
-
return false unless batch && control_asset
-
-
batch.requests.each do |request|
-
next unless request_id_set.include? request.id
-
lane = request.target_asset
-
next unless lane
-
AssetLink.create_edge(control_asset, lane)
-
end
-
-
control_asset.save!
-
return true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
# An aliquot can be considered to be an amount of a material in a liquid. The material could be the DNA
-
# of a sample, or it might be a library (a combination of the DNA sample and a tag).
-
1
class Aliquot < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include Api::Messages::FlowcellIO::AliquotExtensions
-
1
include AliquotIndexer::AliquotScopes
-
-
1
class Receptacle < Asset
-
1
include Transfer::State
-
1
include Aliquot::Remover
-
-
1
has_many :transfer_requests, :class_name => 'TransferRequest', :foreign_key => :target_asset_id
-
1
has_many :transfer_requests_as_source, :class_name => 'TransferRequest', :foreign_key => :asset_id
-
1
has_many :transfer_requests_as_target, :class_name => 'TransferRequest', :foreign_key => :target_asset_id
-
-
1
has_many :requests, :inverse_of => :asset, :foreign_key => :asset_id
-
1
has_one :source_request, :class_name => "Request", :foreign_key => :target_asset_id, :include => :request_metadata
-
1
has_many :requests_as_source, :class_name => 'Request', :foreign_key => :asset_id, :include => :request_metadata
-
1
has_many :requests_as_target, :class_name => 'Request', :foreign_key => :target_asset_id, :include => :request_metadata
-
-
1
def default_state
-
nil
-
end
-
-
1
SAMPLE_PARTIAL = 'assets/samples_partials/asset_samples'
-
-
# A receptacle can hold many aliquots. For example, a multiplexed library tube will contain more than
-
# one aliquot.
-
1
has_many :aliquots, :foreign_key => :receptacle_id, :autosave => true, :dependent => :destroy, :inverse_of => :receptacle, :include => [:tag,:tag2], :order => 'tag2s_aliquots.map_id ASC, tags.map_id ASC'
-
1
has_one :primary_aliquot, :class_name => 'Aliquot', :foreign_key => :receptacle_id, :order => 'created_at ASC', :readonly => true
-
-
# Our receptacle needs to report its tagging status based on the most highly tagged aliquot. This retrieves it
-
1
has_one :most_tagged_aliquot, :class_name => 'Aliquot', :foreign_key => :receptacle_id, :order => 'tag2_id DESC, tag_id DESC', :readonly => true
-
-
# Named scopes for the future
-
1
scope :include_aliquots, -> { includes( :aliquots => [ :sample, :tag, :bait_library ] ) }
-
-
# This is a lambda as otherwise the scope selects Aliquot::Receptacles
-
1
scope :with_aliquots, -> { joins(:aliquots) }
-
-
# Provide some named scopes that will fit with what we've used in the past
-
1
scope :with_sample_id, ->(id) { { :conditions => { :aliquots => { :sample_id => Array(id) } }, :joins => :aliquots } }
-
1
scope :with_sample, ->(sample) { { :conditions => { :aliquots => { :sample_id => Array(sample) } }, :joins => :aliquots } }
-
-
# TODO: Remove these at some point in the future as they're kind of wrong!
-
1
has_one :sample, :through => :primary_aliquot
-
1
deprecate :sample
-
-
1
def sample=(sample)
-
aliquots.clear
-
aliquots << Aliquot.new(:sample => sample)
-
end
-
1
deprecate :sample=
-
-
1
def sample_id
-
primary_aliquot.try(:sample_id)
-
end
-
1
deprecate :sample_id
-
-
1
def sample_id=(sample_id)
-
aliquots.clear
-
aliquots << Aliquot.new(:sample_id => sample_id)
-
end
-
1
deprecate :sample_id=
-
-
1
has_one :get_tag, :through => :primary_aliquot, :source => :tag
-
1
deprecate :get_tag
-
-
1
def tag
-
get_tag.try(:map_id) || ''
-
end
-
1
deprecate :tag
-
-
1
def tags
-
aliquots
-
end
-
1
deprecate :tags
-
-
1
def tag_count
-
# Find the most highly tagged aliquot
-
return 2 if most_tagged_aliquot.tag2_id != Aliquot::UNASSIGNED_TAG
-
return 1 if most_tagged_aliquot.tag_id != Aliquot::UNASSIGNED_TAG
-
0
-
end
-
-
1
def primary_aliquot_if_unique
-
primary_aliquot if aliquots.count == 1
-
end
-
-
1
def type
-
self.class.name.underscore
-
end
-
-
1
def specialized_from_manifest=(*args);end
-
1
def library_information;end
-
1
def library_information=(*args);end
-
-
1
def assign_tag2(tag)
-
aliquots.each do |aliquot|
-
aliquot.tag2 = tag
-
aliquot.save!
-
end
-
end
-
-
1
has_many :studies, :through => :aliquots
-
1
has_many :projects, :through => :aliquots
-
1
has_many :samples, :through => :aliquots
-
-
# Contained samples also works on eg. plate
-
1
alias_attribute :contained_samples, :samples
-
end
-
-
# Something that is aliquotable can be part of an aliquot. So sample and tag are both examples.
-
1
module Aliquotable
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
has_many :aliquots
-
1
has_many :receptacles, :through => :aliquots, :uniq => true
-
# has_one :primary_receptacle, :through => :aliquots, :source => :receptacle, :order => 'aliquots.created_at, aliquots.id ASC'
-
-
1
def primary_receptacle
-
receptacles.order('aliquots.created_at, aliquots.id ASC').first
-
end
-
-
1
has_many :requests, :through => :assets
-
1
has_many :submissions, :through => :requests
-
-
end
-
end
-
-
1
module ClassMethods
-
1
def receptacle_alias(name, options = {}, &block)
-
3
has_many(name, options.merge(:through => :aliquots, :source => :receptacle, :uniq => true), &block)
-
end
-
end
-
end
-
-
1
include Api::AliquotIO::Extensions
-
# An aliquot is held within a receptacle
-
1
belongs_to :receptacle, :class_name => 'Asset'
-
-
# An aliquot can belong to a study and a project.
-
1
belongs_to :study
-
1
belongs_to :project
-
-
# An aliquot is an amount of a sample
-
1
belongs_to :sample
-
-
1
has_one :aliquot_index
-
-
1
scope :include_summary, -> { includes([:sample, :tag, :tag2]) }
-
-
1
def aliquot_index_value
-
aliquot_index.try(:aliquot_index)
-
end
-
-
# It may have a tag but not necessarily. If it does, however, that tag needs to be unique within the receptacle.
-
# To ensure that there can only be one untagged aliquot present in a receptacle we use a special value for tag_id,
-
# rather than NULL which does not work in MySQL. It also works because the unassigned tag ID never gets matched
-
# for a Tag and so the result is nil!
-
1
UNASSIGNED_TAG = -1
-
1
belongs_to :tag
-
1
before_validation { |record| record.tag_id ||= UNASSIGNED_TAG }
-
-
1
belongs_to :tag2, :class_name => 'Tag'
-
1
before_validation { |record| record.tag2_id ||= UNASSIGNED_TAG }
-
-
# Might need to remove these if we get a performance hit
-
1
validates_uniqueness_of :tag_id, :scope => [:receptacle_id, :tag2_id]
-
1
validates_uniqueness_of :tag2_id, :scope => [:receptacle_id, :tag_id]
-
-
1
def untagged?
-
self.tag_id.nil? or self.tag_id == UNASSIGNED_TAG
-
end
-
-
1
def no_tag2?
-
self.tag2_id.nil? or self.tag2_id == UNASSIGNED_TAG
-
end
-
-
1
def tagged?
-
!self.untagged?
-
end
-
-
1
def tag_with_unassigned_behaviour
-
untagged? ? nil : tag_without_unassigned_behaviour
-
end
-
1
alias_method_chain(:tag, :unassigned_behaviour)
-
-
# It may have a bait library but not necessarily.
-
1
belongs_to :bait_library
-
-
# An aliquot can represent a library, which is a processed sample that has been fragmented. In which case it
-
# has a receptacle that held the library aliquot and has an insert size describing the fragment positions.
-
1
class InsertSize < Range
-
1
alias_method :from, :first
-
1
alias_method :to, :last
-
end
-
-
# It can belong to a library asset
-
1
belongs_to :library, :class_name => 'Aliquot::Receptacle'
-
1
composed_of :insert_size, :mapping => [%w{insert_size_from from}, %w{insert_size_to to}], :class_name => 'Aliquot::InsertSize', :allow_nil => true
-
-
# Cloning an aliquot should unset the receptacle ID because otherwise it won't get reassigned. We should
-
# also reset the timestamp information as this is a new aliquot really.
-
1
def dup
-
super.tap do |cloned_aliquot|
-
cloned_aliquot.receptacle_id = nil
-
cloned_aliquot.created_at = nil
-
cloned_aliquot.updated_at = nil
-
end
-
end
-
-
1
def clone
-
raise StandardError, "The Behaviour of clone has changed in Rails 3. Please use dup instead!"
-
end
-
-
# return all aliquots originated from the current one
-
# ie aliquots sharing the sample, tag information, descending the requess graph
-
1
def descendants(include_self=false)
-
(include_self ? self : requests).walk_objects({
-
Aliquot => :receptacle,
-
Receptacle => [:aliquots, :requests_as_source],
-
Request => :target_asset
-
}) do |object|
-
case object
-
when Aliquot
-
# we cut the walk if the new aliquot doesn't "match" the current one
-
object if object =~ self
-
else # other objects
-
[] # are walked but not returned
-
end
-
end
-
end
-
-
# Aliquot are similar if they share the same sample AND the same tag (if they have one: nil acts as a wildcard))
-
1
def =~(object)
-
a, b = [self, object].map { |o| [o.tag_id, o.sample_id, o.tag2_id < 0 ? nil : o.tag2_id ] }
-
a.zip(b).all? { |x, y| (x || y) == (y || x) }
-
end
-
-
1
def matches?(object)
-
# Note: This function is directional, and assumes that the downstream aliquot
-
# is checking the upstream aliquot (or the AliquotRecord)
-
case
-
when self.sample_id != object.sample_id then return false # The samples don't match
-
when object.library_id.present? && (self.library_id != object.library_id) then return false # Our librarys don't match.
-
when object.bait_library_id.present? && (self.bait_library_id != object.bait_library_id) then return false # We have different bait libraries
-
when self.untagged? && object.tagged? then raise StandardError, "Tag missing from downstream aliquot" # The downstream aliquot is untagged, but is tagged upstream. Something is wrong!
-
when object.untagged? && object.no_tag2? then return true # The upstream aliquot was untagged, we don't need to check tags
-
else (object.untagged?||(self.tag_id == object.tag_id)) && (object.no_tag2?||(self.tag2_id == object.tag2_id )) # Both aliquots are tagged, we need to check if they match
-
end
-
end
-
-
# Unlike the above methods, which allow untagged to match with tagged, this looks for exact matches only
-
# only id, timestamps and receptacles are excluded
-
1
def equivalent?(other)
-
[:sample_id, :tag_id, :tag2_id, :library_id, :bait_library_id, :insert_size_from, :insert_size_to, :library_type, :project_id, :study_id].all? do |attrib|
-
self.send(attrib) == other.send(attrib)
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
# The following module is included where we have deprecated behaviours that rely on sample or request.
-
1
module Aliquot::DeprecatedBehaviours
-
1
module Request
-
1
def self.included(base)
-
1
base.class_eval do
-
#Shouldn't be used . Here for compatibility with the previous code
-
#having request having one sample
-
1
has_many :samples, :through => :asset
-
1
deprecate :samples, :sample_ids
-
end
-
end
-
-
1
def tag_number
-
tag.try(:map_id) || ''
-
end
-
1
deprecate :tag_number
-
-
# tags and tag have been moved to the appropriate assets.
-
# I don't think that they are used anywhere else apart
-
# from the batch xml and can therefore probably be removed.
-
# ---
-
# Nope, they are used all over the place.
-
1
def tag
-
self.target_asset.primary_aliquot.try(:tag)
-
end
-
1
deprecate :tag
-
-
1
def tags
-
self.asset.tags
-
end
-
1
deprecate :tags
-
# ---
-
-
1
def sample_name?
-
samples.present?
-
end
-
1
deprecate :sample_name?
-
-
1
def sample_name(default=nil, &block)
-
#return the name of the underlying samples
-
# used mainly for compatibility with the old codebase
-
# # default is used if no smaple
-
# # block is used to aggregate the samples
-
case
-
when samples.size == 0 then default
-
when samples.size == 1 then samples.first.name
-
when block_given? then yield(samples)
-
else samples.map(&:name).join(" | ")
-
end
-
end
-
1
deprecate :sample_name
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
module Aliquot::Remover
-
1
class AliquotRecord
-
# We can't use the aliquot itself, as it will have been destroyed by
-
# the time we want to look at it. The aliquot record mimics
-
# an aliquot in the comparison functions
-
-
1
attr_reader :tag_id, :sample_id, :library_id, :bait_library_id, :tag2_id
-
-
1
def initialize(aliquot)
-
@tag_id = aliquot.tag_id
-
@sample_id = aliquot.sample_id
-
@library_id = aliquot.library_id
-
@bait_library_id = aliquot.bait_library_id
-
@tag2_id = aliquot.tag2_id
-
end
-
-
1
def tagged?
-
!self.untagged?
-
end
-
-
1
def untagged?
-
self.tag_id.nil? or (self.tag_id == Aliquot::UNASSIGNED_TAG)
-
end
-
-
1
def no_tag2?
-
self.tag2_id.nil? or (self.tag2_id == Aliquot::UNASSIGNED_TAG)
-
end
-
-
end
-
-
1
def remove_downstream_aliquots
-
# On the target asset of the failed request.
-
ActiveRecord::Base.transaction do
-
target_aliquots = aliquots.map {|aliquot| AliquotRecord.new(aliquot)}
-
on_downstream_aliquots(target_aliquots)
-
end
-
-
end
-
-
1
def on_downstream_aliquots(aliquots_to_remove)
-
requests_as_source.with_target.each do |request|
-
request.target_asset.process_aliquots(aliquots_to_remove)
-
end
-
end
-
-
1
def process_aliquots(aliquots_to_remove)
-
new_aliquots = remove_matching_aliquots(aliquots_to_remove)
-
on_downstream_aliquots(new_aliquots)
-
end
-
-
1
def remove_matching_aliquots(aliquots_to_remove)
-
aliquots_to_remove.map do |aliquot_to_remove|
-
-
to_remove = aliquots.select do |aliquot|
-
aliquot.matches?(aliquot_to_remove)
-
end
-
-
case
-
when to_remove.count > 1 then raise "Duplicate aliquots detected in asset #{display_name}."
-
when to_remove.count == 1
-
removed_aliquot = AliquotRecord.new(to_remove.first)
-
to_remove.first.destroy
-
removed_aliquot
-
else # We have noting to remove
-
nil
-
end
-
-
end.compact
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
class AliquotIndex < ActiveRecord::Base
-
-
1
belongs_to :aliquot
-
1
belongs_to :lane
-
-
1
validates_presence_of :aliquot
-
1
validates_presence_of :lane
-
1
validates_numericality_of :aliquot_index, :only_integer => true, :greater_than => 0, :less_than_or_equal_to => 999, :allow_blank? => false
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class AliquotIndexer
-
-
1
attr_reader :lane, :aliquots
-
-
1
module AliquotScopes
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :sorted_for_indexing, -> { joins([:tag,:tag2]).order('tag2s_aliquots.map_id ASC, tags.map_id ASC') }
-
end
-
end
-
end
-
-
1
module Indexable
-
1
def index_aliquots
-
# Skip indexing if already present. Makes the call idempotent and also
-
# reduces the downtime required for the initial migration. Race conditions
-
# are unlikely, and will be pretty harmless if they do occur.
-
AliquotIndexer.index(self) unless aliquot_indicies.present?
-
end
-
end
-
-
1
def self.index(lane)
-
new(lane).index
-
end
-
-
1
def initialize(lane)
-
@lane = lane
-
@index = 0
-
end
-
-
1
def phix_map_id
-
return nil unless lane.spiked_in_buffer.present?
-
@phix_map_id ||= lane.spiked_in_buffer.primary_aliquot.tag.try(:map_id)
-
end
-
-
1
def aliquots
-
@aliquots ||= lane.aliquots.reject {|a| a.untagged? }
-
end
-
-
1
def next_index
-
@index += 1
-
next_index if [phix_map_id,configatron.phix_tag.tag_map_id].include?(@index)
-
@index
-
end
-
-
1
def index
-
@lane.aliquot_indicies.build(aliquots.each_with_index.map {|a,i| {:aliquot=>a, :aliquot_index => next_index } })
-
@lane.save
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class Api::AliquotIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::AliquotIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([
-
:uuid_object,
-
{ :sample => :uuid_object} ,
-
{ :study => :uuid_object },
-
{ :project => :uuid_object },
-
{ :tag => :uuid_object },
-
{ :library => :uuid_object },
-
{ :receptacle => :uuid_object }
-
])}
-
end
-
end
-
end
-
1
renders_model(::Aliquot)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
map_attribute_to_json_attribute(:insert_size_from)
-
1
map_attribute_to_json_attribute(:insert_size_to)
-
1
map_attribute_to_json_attribute('library_type')
-
-
1
with_association(:study) do
-
1
map_attribute_to_json_attribute(:url , 'study_url')
-
1
map_attribute_to_json_attribute(:uuid, 'study_uuid')
-
1
map_attribute_to_json_attribute(:id , 'study_internal_id')
-
end
-
-
1
with_association(:project) do
-
1
map_attribute_to_json_attribute(:url , 'project_url')
-
1
map_attribute_to_json_attribute(:uuid, 'project_uuid')
-
1
map_attribute_to_json_attribute(:id , 'project_internal_id')
-
end
-
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:url , 'sample_url')
-
1
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
1
map_attribute_to_json_attribute(:id , 'sample_internal_id')
-
end
-
-
1
with_association(:tag) do
-
1
map_attribute_to_json_attribute(:url , 'tag_url')
-
1
map_attribute_to_json_attribute(:uuid, 'tag_uuid')
-
1
map_attribute_to_json_attribute(:id , 'tag_internal_id')
-
end
-
1
with_association(:receptacle) do
-
1
map_attribute_to_json_attribute(:url , 'receptacle_url')
-
1
map_attribute_to_json_attribute(:uuid, 'receptacle_uuid')
-
1
map_attribute_to_json_attribute(:id , 'receptacle_internal_id')
-
1
map_attribute_to_json_attribute(:type , 'receptacle_type')
-
end
-
-
1
with_association(:library) do
-
1
map_attribute_to_json_attribute(:url , 'library_url')
-
1
map_attribute_to_json_attribute(:uuid, 'library_uuid')
-
1
map_attribute_to_json_attribute(:id, 'library_internal_id')
-
end
-
-
1
with_association(:bait_library) do
-
1
map_attribute_to_json_attribute(:name, 'bait_library_name')
-
1
map_attribute_to_json_attribute(:target_species, 'bait_library_target_species')
-
1
map_attribute_to_json_attribute(:supplier_identifier, 'bait_library_supplier_identifier')
-
1
with_association(:bait_library_supplier) do
-
1
map_attribute_to_json_attribute(:name, 'bait_library_supplier_name')
-
end
-
end
-
#self.related_resources = [ :library_tubes, :requests ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::AssetAuditIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::AssetAuditIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, { :asset => [ :uuid_object, :barcode_prefix ] } ]) }
-
end
-
end
-
end
-
1
renders_model(::AssetAudit)
-
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:message)
-
1
map_attribute_to_json_attribute(:key)
-
1
map_attribute_to_json_attribute(:created_by)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:witnessed_by)
-
-
1
with_association(:asset) do
-
1
map_attribute_to_json_attribute(:uuid, 'plate_uuid')
-
1
map_attribute_to_json_attribute(:barcode, 'plate_barcode')
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'plate_barcode_prefix')
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::AssetLinkIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::AssetLinkIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, { :ancestor => :uuid_object }, { :descendant => :uuid_object }]) }
-
end
-
end
-
end
-
1
renders_model(::AssetLink)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:ancestor) do
-
1
map_attribute_to_json_attribute(:uuid, 'ancestor_uuid')
-
1
map_attribute_to_json_attribute(:id, 'ancestor_internal_id')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes['ancestor_type'] = object.sti_type.tableize unless object.nil?
-
end
-
end
-
-
1
with_association(:descendant) do
-
1
map_attribute_to_json_attribute(:uuid, 'descendant_uuid')
-
1
map_attribute_to_json_attribute(:id, 'descendant_internal_id')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes['descendant_type'] = object.sti_type.tableize unless object.nil?
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.
-
1
class Api::Base
-
# TODO[xxx]: This class is in a state of flux at the moment, please don't hack at this too much!
-
#
-
# Basically this is in a transition as I move more of the behaviour of the API into these model classes,
-
# and out of the controllers, and will eventually be much clearer. And, although this class looks
-
# extremely complex, it's purpose is to make subclasses much, much easier to write and maintain.
-
-
#--
-
# The following block defines the methods used by the Api::BaseController class.
-
#++
-
1
class << self
-
1
def create!(params)
-
model_class.create!(attributes_from_json(params))
-
end
-
-
1
def update_attributes!(object, params)
-
object.update_attributes!(attributes_from_json(params))
-
end
-
-
# Maps the attribute names in the errors to their JSON counterparts, so that the end user gets
-
# the correct information.
-
1
def map_attribute_to_json_attribute_in_errors(attribute_errors)
-
Hash[attribute_errors.map { |a,v| [ json_attribute_for_attribute(*a.to_s.split('.')), v ] }]
-
end
-
end
-
-
#--
-
# This block defines the methods used to convert objects to JSON. You'll find the code that calls this
-
# in lib/api_tools.rb, as well as in the Api::AssetsController.
-
#++
-
1
class << self
-
1
def to_hash(object)
-
# If the object is nil we get a chance to use the 'default' object that was specified. By
-
# default the "default" object is nil, but you can override it for associations through the
-
# with_association(:name, :if_nil_use => :some_method).
-
object ||= default_object
-
return {} if object.nil?
-
-
json_attributes = {}
-
json_attributes["deleted_at"] = Time.now if object.destroyed?
-
-
self.attribute_to_json_attribute_mappings.each do |attribute, json_attribute|
-
json_attributes[ json_attribute ] = object.send(attribute)
-
end
-
self.associations.each do |association, helper|
-
value = object.send(association)
-
json_attributes.update(helper.to_hash(value))
-
helper.newer_than(value, json_attributes['updated_at']) { |timestamp| json_attributes['updated_at'] = timestamp }
-
end
-
self.nested_has_many_associations.each do |association, helper|
-
values = object.send(association)
-
all_targets = values.map do |value|
-
helper.newer_than(value, json_attributes['updated_at']) { |timestamp| json_attributes['updated_at'] = timestamp }
-
helper.to_hash(value)
-
end
-
json_attributes.update({association.to_s => all_targets })
-
end
-
self.related_resources.each do |relation|
-
json_attributes[ relation.to_s ] = File.join(object.url, relation.to_s)
-
end
-
self.extra_json_attribute_handlers.each do |handler|
-
handler.call(object, json_attributes)
-
end
-
json_attributes
-
end
-
-
1
def to_hash_for_list(object)
-
raise StandardError, 'The object is nil, which is highly unexpected!' if object.nil?
-
-
json_attributes = {}
-
self.attribute_to_json_attribute_mappings_for_list.each do |attribute, json_attribute|
-
json_attributes[ json_attribute ] = object.send(attribute)
-
end
-
json_attributes
-
end
-
end
-
-
#--
-
# This code is called when constructing a runtime class for the I/O of a class that does not have
-
# a specific I/O class.
-
#++
-
1
class << self
-
# The default behaviour for any model I/O is to write out all of the columns as they appear. Some of
-
# the columns are ignored, a few manipulated, but mostly it's a direct copy.
-
1
def render_class_for_model(model)
-
render_class = Class.new(self)
-
-
# NOTE: It's quite annoying that you don't have any access to the inheritable class attributes from
-
# within the Class.new block above, so we have to do a separate instance_eval to get it to work.
-
render_class.instance_eval do
-
self.model_class = model
-
-
model.column_names.each do |column|
-
map_attribute_to_json_attribute(column, column) unless [ :descriptor_fields ].include?(column.to_sym)
-
end
-
-
# TODO[xxx]: It's better that some of these are decided at generation, rather than execution, time.
-
extra_json_attributes do |object, json_attributes|
-
json_attributes["uuid"] = object.uuid if object.respond_to?(:uuid)
-
-
# Users and roles
-
if object.respond_to?(:user)
-
json_attributes["user"] = object.user.nil? ? "unknown" : object.user.login
-
end
-
if object.respond_to?(:roles)
-
object.roles.each do |role|
-
json_attributes[role.name.underscore] = role.users.map do |user|
-
{
-
:login => user.login,
-
:email => user.email,
-
:name => user.name
-
}
-
end
-
end
-
end
-
end
-
end
-
return render_class
-
end
-
end
-
-
# The model class that our I/O methods are responsible for
-
1
class_attribute :model_class
-
-
1
def self.renders_model(model)
-
27
self.model_class = model
-
end
-
-
# Contains the mapping from the ActiveRecord attribute to the key in the JSON hash
-
1
class_attribute :attribute_to_json_attribute_mappings, :instance_writer => false
-
1
self.attribute_to_json_attribute_mappings = {}
-
-
# TODO[xxx]: Need to warn about 'id' not being 'internal_id'
-
1
def self.map_attribute_to_json_attribute(attribute, json_attribute = attribute)
-
464
self.attribute_to_json_attribute_mappings = self.attribute_to_json_attribute_mappings.merge(attribute.to_sym => json_attribute.to_s)
-
end
-
-
# Contains a list of resources that are related and should be exposed as URLs
-
1
class_attribute :related_resources
-
1
self.related_resources = []
-
-
# Contains the mapping from the ActiveRecord association to the I/O object that can output it.
-
1
class_attribute :associations, :instance_writer => false
-
1
self.associations = {}
-
-
# Contains the mapping from the ActiveRecord association to the I/O object that can output it.
-
1
class_attribute :nested_has_many_associations
-
1
self.nested_has_many_associations = {}
-
-
1
def self.newer_than(object, timestamp, &block)
-
return if object.nil? or timestamp.nil?
-
modified, object_timestamp = false, ((object.respond_to?(:updated_at) ? object.updated_at : timestamp) || timestamp)
-
timestamp, modified = object_timestamp, true if object_timestamp > timestamp
-
self.associations.each do |association, helper|
-
helper.newer_than(object.send(association), timestamp) { |t| timestamp, modified = t, true }
-
end
-
self.nested_has_many_associations.each do |association, helper|
-
object.send(association).each do |child|
-
helper.newer_than(child, timestamp) { |t| timestamp, modified = t, true }
-
end
-
end
-
yield(timestamp) if modified
-
end
-
-
# Returns the default object to use (by default this is 'nil') and can be overridden by passing
-
# ':if_nil_use => :some_function_that_returns_default_object' to with_association.
-
1
def self.default_object
-
nil
-
end
-
-
1
def self.with_association(association, options = {}, &block)
-
95
association_helper = Class.new(Api::Base)
-
95
association_helper.class_eval(&block)
-
95
association_helper.singleton_class.class_eval do
-
95
alias_method(:default_object, options[:if_nil_use]) if options.key?(:if_nil_use)
-
95
define_method(:lookup_by) { options[:lookup_by] }
-
95
define_method(:association) { association }
-
end
-
95
self.associations = Hash.new if self.associations.empty?
-
95
self.associations[association.to_sym] = association_helper
-
end
-
-
1
def self.with_nested_has_many_association(association, options = {}, &block)
-
4
association_helper = Class.new(Api::Base)
-
4
association_helper.class_eval(&block)
-
4
association_helper.singleton_class.class_eval do
-
4
define_method(:association) { association }
-
end
-
4
self.nested_has_many_associations = Hash.new if self.nested_has_many_associations.empty?
-
4
self.nested_has_many_associations[ association.to_sym ] = association_helper
-
end
-
-
1
def self.performs_lookup?
-
!!self.lookup_by
-
end
-
-
1
def self.lookup_associated_record_from(json_attributes, &block)
-
attributes = convert_json_attributes_to_attributes(json_attributes)
-
return unless attributes.key?(self.lookup_by)
-
conditions = { self.lookup_by => attributes[self.lookup_by] }
-
yield(self.association.to_s.classify.constantize.first(:conditions => conditions))
-
end
-
-
# Contains the mapping from the ActiveRecord attribute to the key in the JSON hash when listing objects
-
1
class_attribute :attribute_to_json_attribute_mappings_for_list
-
-
1
self.attribute_to_json_attribute_mappings_for_list = {
-
:id => 'id',
-
:uuid => 'uuid', # TODO[xxx]: if respond_to?(:uuid)
-
:url => 'url', # TODO[xxx]: if respond_to?(:uuid)
-
:name => 'name' # TODO[xxx]: if respond_to?(:name)
-
}
-
-
# Additional JSON attribute handling, that cannot be done with the simple stuff, should be passed
-
# done through a block
-
1
class_attribute :extra_json_attribute_handlers, :instance_writer => false
-
1
self.extra_json_attribute_handlers = []
-
-
1
def self.extra_json_attributes(&block)
-
13
self.extra_json_attribute_handlers = Array.new if self.extra_json_attribute_handlers.empty?
-
13
self.extra_json_attribute_handlers.push(block)
-
end
-
-
1
class << self
-
1
def attributes_from_json(params)
-
convert_json_attributes_to_attributes(params[self.model_class.name.underscore])
-
end
-
-
1
def convert_json_attributes_to_attributes(json_attributes)
-
return {} if json_attributes.blank?
-
-
attributes = {}
-
self.attribute_to_json_attribute_mappings.each do |attribute, json_attribute|
-
attributes[ attribute ] = json_attributes[ json_attribute ] if json_attributes.key?(json_attribute)
-
end
-
self.associations.each do |association, helper|
-
if helper.performs_lookup?
-
helper.lookup_associated_record_from(json_attributes) do |associated_record|
-
attributes[ :"#{ association }_id" ] = associated_record.try(:id)
-
end
-
else
-
association_attributes = helper.convert_json_attributes_to_attributes(json_attributes)
-
attributes[ :"#{ association }_attributes" ] = association_attributes unless association_attributes.empty?
-
end
-
end
-
attributes
-
end
-
-
1
def json_attribute_for_attribute(attribute_or_association, *rest)
-
json_attribute = self.attribute_to_json_attribute_mappings[ attribute_or_association.to_sym ]
-
if json_attribute.blank?
-
# If we have reached the end of the line, and the attribute_or_association is for what looks like
-
# an association, then we'll look it up without the '_id' and return that value.
-
if attribute_or_association.to_s =~ /_id$/ and rest.empty?
-
association = self.associations[ attribute_or_association.to_s.sub(/_id$/, '').to_sym ]
-
raise StandardError, "Unexpected association #{ attribute_or_association.inspect }" if association.nil?
-
return association.json_attribute_for_attribute(:name)
-
end
-
json_attribute = self.associations[ attribute_or_association.to_sym ].json_attribute_for_attribute(*rest)
-
end
-
raise StandardError, "Unexpected attribute #{ attribute_or_association.inspect } does not appear to be mapped" if json_attribute.blank?
-
json_attribute
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Api::BatchIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::BatchIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes( [ :uuid_object, :user, :assignee, { :pipeline => :uuid_object }] ) }
-
end
-
end
-
end
-
-
1
renders_model(::Batch)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:state)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:production_state)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:user) do
-
1
map_attribute_to_json_attribute(:login, 'created_by')
-
end
-
-
1
with_association(:assignee) do
-
1
map_attribute_to_json_attribute(:login, 'assigned_to')
-
end
-
-
1
with_association(:pipeline) do
-
1
map_attribute_to_json_attribute(:name, 'pipeline_name')
-
1
map_attribute_to_json_attribute(:uuid, 'pipeline_uuid')
-
1
map_attribute_to_json_attribute(:id, 'pipeline_internal_id')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Api::BatchRequestIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::BatchRequestIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([ :uuid_object, { :request => [ :uuid_object, :request_type, { :asset => :uuid_object }, { :target_asset => :uuid_object } ] }, { :batch => :uuid_object } ] ) }
-
end
-
end
-
end
-
1
renders_model(::BatchRequest)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:batch) do
-
1
map_attribute_to_json_attribute(:uuid, 'batch_uuid')
-
1
map_attribute_to_json_attribute(:id, 'batch_internal_id')
-
end
-
-
1
with_association(:request) do
-
1
map_attribute_to_json_attribute(:uuid, 'request_uuid')
-
1
map_attribute_to_json_attribute(:id, 'request_internal_id')
-
-
1
with_association(:request_type) do
-
1
map_attribute_to_json_attribute(:name, 'request_type')
-
end
-
-
1
with_association(:asset) do
-
1
map_attribute_to_json_attribute(:uuid, 'source_asset_uuid')
-
1
map_attribute_to_json_attribute(:id, 'source_asset_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'source_asset_name')
-
end
-
-
1
with_association(:target_asset) do
-
1
map_attribute_to_json_attribute(:uuid, 'target_asset_uuid')
-
1
map_attribute_to_json_attribute(:id, 'target_asset_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'target_asset_name')
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::EventIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::EventIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, { :eventful => :uuid_object } ] ) }
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"event"
-
end
-
-
1
def render_class
-
Api::EventIO
-
end
-
end
-
-
1
renders_model(::Event)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:message)
-
1
map_attribute_to_json_attribute(:family)
-
1
map_attribute_to_json_attribute(:identifier)
-
1
map_attribute_to_json_attribute(:location)
-
1
map_attribute_to_json_attribute(:actioned)
-
1
map_attribute_to_json_attribute(:content)
-
1
map_attribute_to_json_attribute(:created_by)
-
1
map_attribute_to_json_attribute(:of_interest_to)
-
1
map_attribute_to_json_attribute(:descriptor_key)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:created_at)
-
-
1
with_association(:eventful) do
-
1
map_attribute_to_json_attribute(:uuid, 'eventful_uuid')
-
1
map_attribute_to_json_attribute(:id, 'eventful_internal_id')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes['eventful_type'] = object.class.name.tableize
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Api::LaneIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::LaneIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, :barcode_prefix ] ) }
-
end
-
end
-
-
1
def related_resources
-
['parents']
-
end
-
end
-
1
renders_model(::Lane)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:external_release)
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
-
1
self.related_resources = [ :requests ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Api::LibraryTubeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::LibraryTubeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([
-
:uuid_object,
-
:barcode_prefix, {
-
:source_request => [:uuid_object, :request_metadata],
-
:primary_aliquot => { :sample => :uuid_object, :tag => [ :uuid_object, { :tag_group => :uuid_object } ] }
-
},
-
:scanned_into_lab_event
-
])}
-
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"library_tube"
-
end
-
end
-
-
1
renders_model(::LibraryTube)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:closed)
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode)
-
1
map_attribute_to_json_attribute(:concentration)
-
1
map_attribute_to_json_attribute(:volume)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:public_name)
-
-
1
with_association(:scanned_into_lab_event) do
-
1
map_attribute_to_json_attribute(:content, 'scanned_in_date')
-
end
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
-
1
with_association(:primary_aliquot_if_unique) do
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
1
map_attribute_to_json_attribute(:id, 'sample_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'sample_name')
-
end
-
-
1
with_association(:tag) do
-
1
map_attribute_to_json_attribute(:uuid , 'tag_uuid')
-
1
map_attribute_to_json_attribute(:id , 'tag_internal_id')
-
1
map_attribute_to_json_attribute(:oligo , 'expected_sequence')
-
1
map_attribute_to_json_attribute(:map_id, 'tag_map_id')
-
-
1
with_association(:tag_group) do
-
1
map_attribute_to_json_attribute(:name, 'tag_group_name')
-
1
map_attribute_to_json_attribute(:uuid, 'tag_group_uuid')
-
1
map_attribute_to_json_attribute(:id , 'tag_group_internal_id')
-
end
-
end
-
end
-
-
1
with_association(:source_request) do
-
1
map_attribute_to_json_attribute(:id, 'source_request_internal_id')
-
1
map_attribute_to_json_attribute(:uuid, 'source_request_uuid')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["read_length"] = object.request_metadata.read_length if object.is_a?(SequencingRequest)
-
json_attributes["library_type"] = object.request_metadata.library_type if object.is_a?(LibraryCreationRequest)
-
json_attributes["fragment_size_required_from"] = object.request_metadata.fragment_size_required_from if object.respond_to?(:fragment_size_required_from)
-
json_attributes["fragment_size_required_to"] = object.request_metadata.fragment_size_required_to if object.respond_to?(:fragment_size_required_to)
-
end
-
end
-
-
1
self.related_resources = [ :lanes, :requests ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Api::Messages::BarcodeIO < Api::Base
-
-
1
renders_model(::Asset)
-
-
1
map_attribute_to_json_attribute(:uuid,'barcodable_uuid')
-
1
map_attribute_to_json_attribute(:sti_type,'barcodable_type')
-
1
map_attribute_to_json_attribute(:ean13_barcode,'machine_readable_barcode')
-
1
map_attribute_to_json_attribute(:sanger_human_barcode,'human_readable_barcode')
-
1
map_attribute_to_json_attribute(:barcode_type,'barcode_type')
-
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
class Api::Messages::FlowcellIO < Api::Base
-
-
MANUAL_QC_BOOLS = {'passed'=>true,'failed'=>false }
-
-
module LaneExtensions # Included in SequencingRequest
-
-
def self.included(base)
-
base.class_eval do
-
-
def position
-
batch_request.position
-
end
-
-
def mx_library
-
asset.external_identifier
-
end
-
-
def manual_qc
-
MANUAL_QC_BOOLS[target_asset.try(:qc_state)]
-
end
-
-
def flowcell_identifier
-
"Chip Barcode"
-
end
-
-
def flowcell_barcode
-
lab_events.each {|e| e.descriptor_value_for(flowcell_identifier).tap {|bc| return bc unless bc.nil? } }
-
end
-
-
def samples
-
some_untagged = target_asset.aliquots.any?(&:untagged?)
-
target_asset.aliquots.reject do |a|
-
(spiked_in_buffer.present? && spiked_in_buffer.primary_aliquot =~ a) or
-
some_untagged && a.tagged? # Reproduces behaviour of batch.xml. Needed due to odd legacy data
-
end
-
1
end
-
-
delegate :spiked_in_buffer, :external_release, :to=>:target_asset, :allow_nil => true
-
-
1
def controls
-
spiked_in_buffer.present? ? spiked_in_buffer.aliquots : []
-
end
-
-
1
def lane_identifier
-
target_asset_id
-
end
-
-
1
def product_line
-
return nil if request_type.product_line.nil?
-
request_type.product_line.name
-
end
-
-
1
def request_purpose_key
-
request_purpose.try(:key)
-
end
-
-
end
-
end
-
end
-
-
module ControlLaneExtensions
-
def self.included(base)
-
1
base.class_eval do
-
-
1
def position
-
batch_request.position
-
end
-
-
1
def mx_library
-
asset.external_identifier||"UNKNOWN"
-
end
-
-
1
def manual_qc
-
MANUAL_QC_BOOLS[target_asset.try(:qc_state)]
-
end
-
-
1
def samples
-
return []
-
end
-
-
1
def product_line
-
nil
-
end
-
-
1
def spiked_in_buffer
-
false
-
end
-
1
def external_release
-
false
-
end
-
-
1
def controls
-
asset.aliquots
-
end
-
-
1
def lane_identifier
-
'control_lane'
-
end
-
-
1
def request_purpose_key
-
request_purpose.try(:key)
-
end
-
-
end
-
end
-
end
-
-
module AliquotExtensions
-
module ClassMethods
-
end
-
-
def self.included(base)
-
base.class_eval do
-
extend ClassMethods
-
-
def aliquot_type
-
tag.present? ? 'library_indexed' : 'library'
-
end
-
-
def control_aliquot_type
-
tag.present? ? 'library_indexed_spike' : 'library_control'
-
end
-
-
def external_library_id
-
library.external_identifier
-
end
-
-
end
-
end
-
end
-
-
module ProjectExtensions
-
module ClassMethods
-
end
-
-
def self.included(base)
-
base.class_eval do
-
extend ClassMethods
-
-
def project_cost_code_for_uwh
-
project_cost_code.length > 20 ? 'Custom' : project_cost_code
-
end
-
end
-
end
-
end
-
-
module Extensions
-
module ClassMethods
-
end
-
-
def self.included(base)
-
base.class_eval do
-
extend ClassMethods
-
-
scope :including_associations_for_json, -> { includes([ :uuid_object, :user, :assignee, { :pipeline => :uuid_object }])}
-
-
def flowcell_barcode
-
requests.first.flowcell_barcode
-
end
-
-
def read_length
-
requests.first.request_metadata.read_length
-
end
-
# We alias is as the json generator assumes each method is called only once.
-
alias :reverse_read_length :read_length
-
-
def lanes; requests; end
-
-
end
-
end
-
end
-
-
renders_model(::Batch)
-
-
map_attribute_to_json_attribute(:flowcell_barcode)
-
map_attribute_to_json_attribute(:id,'flowcell_id')
-
map_attribute_to_json_attribute(:read_length,'forward_read_length')
-
map_attribute_to_json_attribute(:reverse_read_length,'reverse_read_length')
-
-
map_attribute_to_json_attribute(:updated_at)
-
-
with_nested_has_many_association(:lanes) do # actually requests
-
-
map_attribute_to_json_attribute(:manual_qc)
-
map_attribute_to_json_attribute(:position)
-
map_attribute_to_json_attribute(:priority)
-
map_attribute_to_json_attribute(:mx_library,'id_pool_lims')
-
map_attribute_to_json_attribute(:external_release,'external_release')
-
map_attribute_to_json_attribute(:lane_identifier, 'entity_id_lims')
-
map_attribute_to_json_attribute(:product_line,'team')
-
map_attribute_to_json_attribute(:request_purpose_key,'purpose')
-
-
with_nested_has_many_association(:samples) do # actually aliquots
-
-
map_attribute_to_json_attribute(:aliquot_index_value, 'tag_index')
-
-
with_association(:tag) do
-
map_attribute_to_json_attribute(:oligo, 'tag_sequence')
-
map_attribute_to_json_attribute(:tag_group_id, 'tag_set_id_lims')
-
with_association(:tag_group) do
-
map_attribute_to_json_attribute(:name, 'tag_set_name')
-
end
-
map_attribute_to_json_attribute(:map_id, 'tag_identifier')
-
end
-
with_association(:tag2) do
-
map_attribute_to_json_attribute(:oligo, 'tag2_sequence')
-
map_attribute_to_json_attribute(:tag_group_id, 'tag2_set_id_lims')
-
with_association(:tag_group) do
-
map_attribute_to_json_attribute(:name, 'tag2_set_name')
-
end
-
map_attribute_to_json_attribute(:map_id, 'tag2_identifier')
-
end
-
map_attribute_to_json_attribute(:library_type, 'pipeline_id_lims')
-
with_association(:bait_library) do
-
map_attribute_to_json_attribute(:name, 'bait_name')
-
end
-
map_attribute_to_json_attribute(:insert_size_from, 'requested_insert_size_from')
-
map_attribute_to_json_attribute(:insert_size_to, 'requested_insert_size_to')
-
with_association(:sample) do
-
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
end
-
with_association(:study) do
-
map_attribute_to_json_attribute(:uuid, 'study_uuid')
-
end
-
with_association(:project) do
-
map_attribute_to_json_attribute(:project_cost_code_for_uwh, 'cost_code')
-
map_attribute_to_json_attribute(:r_and_d?, 'is_r_and_d')
-
end
-
map_attribute_to_json_attribute(:external_library_id, 'id_library_lims')
-
map_attribute_to_json_attribute(:library_id, 'legacy_library_id')
-
map_attribute_to_json_attribute(:aliquot_type,'entity_type')
-
end
-
-
with_nested_has_many_association(:controls) do
-
with_association(:tag) do
-
map_attribute_to_json_attribute(:map_id, 'tag_index')
-
map_attribute_to_json_attribute(:oligo, 'tag_sequence')
-
map_attribute_to_json_attribute(:tag_group_id, 'tag_set_id_lims')
-
with_association(:tag_group) do
-
map_attribute_to_json_attribute(:name, 'tag_set_name')
-
end
-
end
-
map_attribute_to_json_attribute(:library_type, 'pipeline_id_lims')
-
with_association(:sample) do
-
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
end
-
with_association(:study) do
-
map_attribute_to_json_attribute(:uuid, 'study_uuid')
-
end
-
map_attribute_to_json_attribute(:library_id, 'legacy_library_id')
-
map_attribute_to_json_attribute(:external_library_id, 'id_library_lims')
-
map_attribute_to_json_attribute(:control_aliquot_type,'entity_type')
-
end
-
end
-
-
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Api::Messages::FluidigmPlateIO < Api::Base
-
-
1
module WellExtensions
-
-
1
def cost_code
-
return nil if primary_aliquot.nil?
-
primary_aliquot.project.project_cost_code
-
end
-
-
1
def primary_sample_uuid
-
return nil if primary_aliquot.nil?
-
primary_aliquot.sample.uuid
-
end
-
-
1
def primary_study_uuid
-
return nil if primary_aliquot.nil?
-
primary_aliquot.study.uuid
-
end
-
-
1
def qc_state # Untracked for the moment
-
nil
-
end
-
-
end
-
-
1
renders_model(::Plate)
-
-
1
map_attribute_to_json_attribute(:id,'id_flgen_plate_lims')
-
1
map_attribute_to_json_attribute(:sanger_human_barcode,'plate_barcode_lims')
-
1
map_attribute_to_json_attribute(:fluidigm_barcode,'plate_barcode')
-
1
map_attribute_to_json_attribute(:uuid,'plate_uuid_lims')
-
1
map_attribute_to_json_attribute(:size,'plate_size')
-
1
map_attribute_to_json_attribute(:updated_at,'last_updated') # We do it for the whole plate to ensure the message has a timestamp
-
1
map_attribute_to_json_attribute(:occupied_well_count,'plate_size_occupied')
-
-
1
with_nested_has_many_association(:wells) do
-
1
map_attribute_to_json_attribute(:map_description, 'well_label')
-
1
map_attribute_to_json_attribute(:uuid, 'well_uuid_lims')
-
1
map_attribute_to_json_attribute(:cost_code, 'cost_code')
-
1
map_attribute_to_json_attribute(:primary_sample_uuid, 'sample_uuid')
-
1
map_attribute_to_json_attribute(:primary_study_uuid, 'study_uuid')
-
1
map_attribute_to_json_attribute(:qc_state, 'qc_state')
-
end
-
-
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
class Api::MultiplexedLibraryTubeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::MultiplexedLibraryTubeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, :barcode_prefix, :scanned_into_lab_event ]) }
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def related_resources
-
['parents','children','requests']
-
end
-
-
1
def url_name
-
"multiplexed_library_tube"
-
end
-
end
-
-
1
renders_model(::MultiplexedLibraryTube)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:concentration)
-
1
map_attribute_to_json_attribute(:volume)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:closed)
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:public_name)
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
-
1
with_association(:scanned_into_lab_event) do
-
1
map_attribute_to_json_attribute(:content, 'scanned_in_date')
-
end
-
-
1
self.related_resources = [ :lanes, :requests ]
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class Api::OrderIO < Api::Base
-
1
renders_model(::Order)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:template_name)
-
1
map_attribute_to_json_attribute(:comments)
-
-
#with_association(:submission) do
-
#map_attribute_to_json_attribute(:uuid , 'submission_uuid')
-
#map_attribute_to_json_attribute(:id , 'submission_internal_id')
-
#map_attribute_to_json_attribute(:name , 'submission_name')
-
#end
-
-
1
with_association(:project) do
-
1
map_attribute_to_json_attribute(:uuid , 'project_uuid')
-
1
map_attribute_to_json_attribute(:id , 'project_internal_id')
-
1
map_attribute_to_json_attribute(:name , 'project_name')
-
end
-
-
1
with_association(:study) do
-
1
map_attribute_to_json_attribute(:uuid , 'study_uuid')
-
1
map_attribute_to_json_attribute(:id , 'study_internal_id')
-
1
map_attribute_to_json_attribute(:name , 'study_name')
-
end
-
-
1
with_association(:submission) do
-
1
map_attribute_to_json_attribute(:uuid , 'submission_uuid')
-
1
map_attribute_to_json_attribute(:id , 'submission_internal_id')
-
#map_attribute_to_json_attribute(:name , 'submission_name')
-
end
-
-
1
with_association(:user) do
-
1
map_attribute_to_json_attribute(:login , 'created_by')
-
end
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["asset_uuids"] = object.asset_uuids
-
json_attributes["request_options"] = object.request_options_structured unless object.request_options_structured.blank?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
class Api::PacBioLibraryTubeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::PacBioLibraryTubeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, :barcode_prefix, :scanned_into_lab_event ]) }
-
end
-
end
-
end
-
-
1
renders_model(::PacBioLibraryTube)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:concentration)
-
1
map_attribute_to_json_attribute(:volume)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:closed)
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:public_name)
-
-
1
with_association(:pac_bio_library_tube_metadata) do
-
1
map_attribute_to_json_attribute(:prep_kit_barcode)
-
1
map_attribute_to_json_attribute(:binding_kit_barcode)
-
1
map_attribute_to_json_attribute(:smrt_cells_available)
-
1
map_attribute_to_json_attribute(:movie_length)
-
1
map_attribute_to_json_attribute(:protocol)
-
end
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
-
1
with_association(:scanned_into_lab_event) do
-
1
map_attribute_to_json_attribute(:content, 'scanned_in_date')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Api::PlateIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::PlateIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, :plate_metadata, :barcode_prefix, :location, { :plate_purpose => :uuid_object } ]) }
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"plate"
-
end
-
end
-
1
renders_model(::Plate)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:size)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:plate_metadata) do
-
1
map_attribute_to_json_attribute(:infinium_barcode)
-
1
map_attribute_to_json_attribute(:fluidigm_barcode)
-
end
-
-
1
with_association(:location) do
-
1
map_attribute_to_json_attribute(:name, 'location')
-
end
-
-
1
with_association(:plate_purpose, :if_nil_use => :stock_plate_purpose) do
-
1
map_attribute_to_json_attribute(:name, 'plate_purpose_name')
-
1
map_attribute_to_json_attribute(:id, 'plate_purpose_internal_id')
-
1
map_attribute_to_json_attribute(:uuid, 'plate_purpose_uuid')
-
-
1
def self.stock_plate_purpose
-
PlatePurpose.find_by_name('Stock Plate')
-
end
-
end
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::PlatePurposeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::PlatePurposeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"plate_purpose"
-
end
-
end
-
-
1
renders_model(::PlatePurpose)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::ProjectIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::ProjectIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([
-
:uuid_object, {
-
:project_metadata => [ :project_manager, :budget_division ],
-
:roles => :users
-
}
-
])}
-
end
-
end
-
-
1
def related_resources
-
['studies']
-
end
-
end
-
-
1
renders_model(::Project)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:approved)
-
1
map_attribute_to_json_attribute(:state)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:project_metadata) do
-
1
with_association(:project_manager, :lookup_by => :name) do
-
1
map_attribute_to_json_attribute(:name , 'project_manager')
-
end
-
1
map_attribute_to_json_attribute(:project_cost_code , 'cost_code')
-
1
map_attribute_to_json_attribute(:funding_comments , 'funding_comments')
-
1
map_attribute_to_json_attribute(:collaborators , 'collaborators')
-
1
map_attribute_to_json_attribute(:external_funding_source , 'external_funding_source')
-
1
with_association(:budget_division, :lookup_by => :name) do
-
1
map_attribute_to_json_attribute(:name , 'budget_division')
-
end
-
1
map_attribute_to_json_attribute(:sequencing_budget_cost_centre , 'budget_cost_centre')
-
1
map_attribute_to_json_attribute(:project_funding_model , 'funding_model')
-
end
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["uuid"] = object.uuid if object.respond_to?(:uuid)
-
-
# Users and roles
-
if object.respond_to?(:user)
-
json_attributes["user"] = object.user.nil? ? "unknown" : object.user.login
-
end
-
if object.respond_to?(:roles)
-
object.roles.each do |role|
-
json_attributes[role.name.underscore] = role.users.map do |user|
-
{
-
:login => user.login,
-
:email => user.email,
-
:name => user.name
-
}
-
end
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
class Api::PulldownMultiplexedLibraryTubeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::PulldownMultiplexedLibraryTubeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, :barcode_prefix, :scanned_into_lab_event ]) }
-
end
-
end
-
end
-
-
1
renders_model(::PulldownMultiplexedLibraryTube)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:concentration)
-
1
map_attribute_to_json_attribute(:volume)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:closed)
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:public_name)
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
-
1
with_association(:scanned_into_lab_event) do
-
1
map_attribute_to_json_attribute(:content, 'scanned_in_date')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class Api::ReferenceGenomeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::ReferenceGenomeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"reference_genome"
-
end
-
end
-
-
1
renders_model(::ReferenceGenome)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::RequestIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::RequestIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([
-
:uuid_object,
-
:request_type,
-
:request_metadata,
-
:user, {
-
:asset => [
-
:uuid_object,
-
:barcode_prefix,
-
{ :primary_aliquot => { :sample => :uuid_object } }
-
],
-
:target_asset => [
-
:uuid_object,
-
:barcode_prefix,
-
{ :primary_aliquot => { :sample => :uuid_object } }
-
],
-
:initial_study =>:uuid_object,
-
:initial_project => :uuid_object
-
}
-
])}
-
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"request" # frozen for subclass of the API
-
end
-
end
-
-
1
renders_model(::Request)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:state)
-
1
map_attribute_to_json_attribute(:priority)
-
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["read_length"] = object.request_metadata.read_length if object.is_a?(SequencingRequest)
-
json_attributes["library_type"] = object.request_metadata.library_type if object.is_a?(LibraryCreationRequest)
-
json_attributes["fragment_size_required_from"] = object.request_metadata.fragment_size_required_from if object.request_metadata.respond_to?(:fragment_size_required_from)
-
json_attributes["fragment_size_required_to"] = object.request_metadata.fragment_size_required_to if object.request_metadata.respond_to?(:fragment_size_required_to)
-
end
-
-
1
with_association(:user) do
-
1
map_attribute_to_json_attribute(:login , 'user')
-
end
-
-
1
with_association(:submission) do
-
1
map_attribute_to_json_attribute(:uuid, 'submission_uuid')
-
1
map_attribute_to_json_attribute(:id , 'submission_internal_id')
-
1
map_attribute_to_json_attribute(:url , 'submission_url')
-
end
-
-
1
with_association(:initial_study) do
-
1
map_attribute_to_json_attribute(:url , 'study_url')
-
1
map_attribute_to_json_attribute(:uuid, 'study_uuid')
-
1
map_attribute_to_json_attribute(:id , 'study_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'study_name')
-
end
-
-
1
with_association(:initial_project) do
-
1
map_attribute_to_json_attribute(:url , 'project_url')
-
1
map_attribute_to_json_attribute(:uuid, 'project_uuid')
-
1
map_attribute_to_json_attribute(:id , 'project_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'project_name')
-
end
-
-
1
with_association(:asset) do
-
1
map_attribute_to_json_attribute(:uuid , 'source_asset_uuid')
-
1
map_attribute_to_json_attribute(:id , 'source_asset_internal_id')
-
1
map_attribute_to_json_attribute(:name , 'source_asset_name')
-
1
map_attribute_to_json_attribute(:barcode , 'source_asset_barcode')
-
1
map_attribute_to_json_attribute(:qc_state , 'source_asset_state')
-
1
map_attribute_to_json_attribute(:closed , 'source_asset_closed')
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode, 'source_asset_two_dimensional_barcode')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["source_asset_type"] = object.sti_type.tableize unless object.nil?
-
end
-
-
1
with_association(:primary_aliquot_if_unique) do
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:uuid, 'source_asset_sample_uuid')
-
1
map_attribute_to_json_attribute(:id , 'source_asset_sample_internal_id')
-
end
-
end
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'source_asset_barcode_prefix')
-
end
-
end
-
-
1
with_association(:target_asset) do
-
1
map_attribute_to_json_attribute(:uuid , 'target_asset_uuid')
-
1
map_attribute_to_json_attribute(:id , 'target_asset_internal_id')
-
1
map_attribute_to_json_attribute(:name , 'target_asset_name')
-
1
map_attribute_to_json_attribute(:barcode , 'target_asset_barcode')
-
1
map_attribute_to_json_attribute(:qc_state , 'target_asset_state')
-
1
map_attribute_to_json_attribute(:closed , 'target_asset_closed')
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode, 'target_asset_two_dimensional_barcode')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["target_asset_type"] = object.sti_type.tableize unless object.nil?
-
end
-
-
1
with_association(:primary_aliquot_if_unique) do
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:uuid, 'target_asset_sample_uuid')
-
1
map_attribute_to_json_attribute(:id , 'target_asset_sample_internal_id')
-
end
-
end
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'target_asset_barcode_prefix')
-
end
-
end
-
-
1
with_association(:request_type) do
-
1
map_attribute_to_json_attribute(:name, 'request_type')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Api::SampleIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::SampleIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, { :sample_metadata => :reference_genome }, { :studies => [ :study_metadata, :uuid_object ] } ])}
-
1
alias_method(:json_root, :url_name)
-
end
-
end
-
-
1
def url_name
-
"sample"
-
end
-
end
-
-
1
renders_model(::Sample)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:consent_withdrawn)
-
1
map_attribute_to_json_attribute(:new_name_format)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:sanger_sample_id)
-
1
map_attribute_to_json_attribute(:control)
-
1
map_attribute_to_json_attribute(:sample_manifest_id)
-
1
map_attribute_to_json_attribute(:empty_supplier_sample_name)
-
1
map_attribute_to_json_attribute(:updated_by_manifest)
-
-
1
with_association(:sample_metadata) do
-
1
map_attribute_to_json_attribute(:organism)
-
1
map_attribute_to_json_attribute(:cohort)
-
1
map_attribute_to_json_attribute(:country_of_origin)
-
1
map_attribute_to_json_attribute(:geographical_region)
-
1
map_attribute_to_json_attribute(:ethnicity)
-
1
map_attribute_to_json_attribute(:volume)
-
1
map_attribute_to_json_attribute(:supplier_plate_id)
-
1
map_attribute_to_json_attribute(:mother)
-
1
map_attribute_to_json_attribute(:father)
-
1
map_attribute_to_json_attribute(:replicate)
-
1
map_attribute_to_json_attribute(:gc_content)
-
1
map_attribute_to_json_attribute(:gender)
-
1
map_attribute_to_json_attribute(:dna_source)
-
1
map_attribute_to_json_attribute(:sample_public_name)
-
1
map_attribute_to_json_attribute(:sample_common_name)
-
1
map_attribute_to_json_attribute(:sample_strain_att)
-
1
map_attribute_to_json_attribute(:sample_taxon_id)
-
1
map_attribute_to_json_attribute(:sample_ebi_accession_number)
-
1
map_attribute_to_json_attribute(:sample_description)
-
1
map_attribute_to_json_attribute(:sample_sra_hold)
-
1
with_association(:reference_genome, :lookup_by => :name) do
-
1
map_attribute_to_json_attribute(:name, 'reference_genome')
-
end
-
1
map_attribute_to_json_attribute(:supplier_name)
-
1
map_attribute_to_json_attribute(:donor_id)
-
end
-
-
1
self.related_resources = [ :sample_tubes ]
-
-
1
extra_json_attributes do |object, json_attributes|
-
if json_attributes['reference_genome'].blank?
-
json_attributes['reference_genome'] = nil
-
end
-
end
-
-
# Whenever we create samples through the API we also need to register a sample tube too. The user
-
# can then retrieve the sample tube information through the API.
-
1
def self.create!(parameters)
-
super.tap do |sample|
-
Tube::Purpose.standard_sample_tube.create!.aliquots.create!(:sample => sample)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class Api::SampleTubeIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::SampleTubeIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([ :uuid_object, :barcode_prefix, { :primary_aliquot => { :sample => :uuid_object } }, :scanned_into_lab_event ])}
-
end
-
end
-
end
-
1
renders_model(::SampleTube)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:barcode)
-
1
map_attribute_to_json_attribute(:qc_state)
-
1
map_attribute_to_json_attribute(:closed)
-
1
map_attribute_to_json_attribute(:two_dimensional_barcode)
-
1
map_attribute_to_json_attribute(:concentration)
-
1
map_attribute_to_json_attribute(:volume)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:scanned_into_lab_event) do
-
1
map_attribute_to_json_attribute(:content, 'scanned_in_date')
-
end
-
-
1
with_association(:barcode_prefix) do
-
1
map_attribute_to_json_attribute(:prefix, 'barcode_prefix')
-
end
-
-
1
with_association(:primary_aliquot_if_unique) do
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
1
map_attribute_to_json_attribute(:id , 'sample_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'sample_name')
-
end
-
end
-
-
1
self.related_resources = [ :library_tubes, :requests ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015 Genome Research Ltd.
-
1
class Api::StudyIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::StudyIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([
-
:uuid_object, {
-
:study_metadata => [:faculty_sponsor, :reference_genome, :study_type, :data_release_study_type],
-
:roles => :users
-
}
-
])}
-
end
-
end
-
-
1
def render_class
-
Api::StudyIO
-
end
-
end
-
-
1
renders_model(::Study)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:ethically_approved)
-
1
map_attribute_to_json_attribute(:state)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["abbreviation"] = object.abbreviation
-
-
object.roles.each do |role|
-
json_attributes[role.name.downcase.gsub(/\s+/, '_')] = role.user_role_bindings.map do |user_role|
-
{ :login => user_role.user.login, :email => user_role.user.email, :name => user_role.user.name }.tap do
-
json_attributes['updated_at'] ||= user_role.updated_at
-
json_attributes['updated_at'] = user_role.updated_at if json_attributes['updated_at'] < user_role.updated_at
-
end
-
end
-
end if object.respond_to?(:roles)
-
end
-
-
1
with_association(:study_metadata) do
-
1
with_association(:faculty_sponsor, :lookup_by => :name) do
-
1
map_attribute_to_json_attribute(:name, 'sac_sponsor')
-
end
-
-
1
with_association(:reference_genome, :lookup_by => :name) do
-
1
map_attribute_to_json_attribute(:name, 'reference_genome')
-
end
-
1
map_attribute_to_json_attribute(:prelim_id, 'prelim_id')
-
1
map_attribute_to_json_attribute(:study_ebi_accession_number, 'accession_number')
-
1
map_attribute_to_json_attribute(:study_description , 'description')
-
1
map_attribute_to_json_attribute(:study_abstract , 'abstract')
-
1
with_association(:study_type, :lookup_by => :name) do
-
1
map_attribute_to_json_attribute(:name , 'study_type')
-
end
-
-
1
map_attribute_to_json_attribute(:study_project_id, 'ena_project_id')
-
1
map_attribute_to_json_attribute(:study_study_title, 'study_title')
-
1
map_attribute_to_json_attribute(:study_sra_hold, 'study_visibility')
-
-
1
map_attribute_to_json_attribute(:contaminated_human_dna)
-
1
map_attribute_to_json_attribute(:contains_human_dna)
-
1
map_attribute_to_json_attribute(:commercially_available)
-
1
with_association(:data_release_study_type, :lookup_by => :name ) do
-
1
map_attribute_to_json_attribute(:name , 'data_release_sort_of_study')
-
end
-
1
map_attribute_to_json_attribute(:remove_x_and_autosomes?, 'remove_x_and_autosomes')
-
1
map_attribute_to_json_attribute(:separate_y_chromosome_data)
-
-
1
map_attribute_to_json_attribute(:data_release_strategy)
-
1
map_attribute_to_json_attribute(:ega_dac_accession_number)
-
1
map_attribute_to_json_attribute(:array_express_accession_number)
-
1
map_attribute_to_json_attribute(:ega_policy_accession_number)
-
-
1
map_attribute_to_json_attribute(:data_release_timing)
-
1
map_attribute_to_json_attribute(:data_release_delay_period)
-
1
map_attribute_to_json_attribute(:data_release_delay_reason)
-
-
1
map_attribute_to_json_attribute(:data_access_group)
-
-
1
map_attribute_to_json_attribute(:bam, 'alignments_in_bam')
-
1
map_attribute_to_json_attribute(:prelim_id)
-
1
map_attribute_to_json_attribute(:hmdmc_approval_number,'hmdmc_number')
-
end
-
-
1
self.related_resources = [ :samples, :projects ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::StudySampleIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::StudySampleIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, {:study => :uuid_object }, {:sample => :uuid_object } ]) }
-
end
-
end
-
end
-
-
1
renders_model(::StudySample)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:id , 'sample_internal_id')
-
1
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
end
-
-
1
with_association(:study) do
-
1
map_attribute_to_json_attribute(:id , 'study_internal_id')
-
1
map_attribute_to_json_attribute(:uuid, 'study_uuid')
-
end
-
-
1
self.related_resources = [ :samples, :studies ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::SubmissionIO < Api::Base
-
1
renders_model(::Submission)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
1
map_attribute_to_json_attribute(:state)
-
1
map_attribute_to_json_attribute(:message)
-
-
1
with_association(:user) do
-
1
map_attribute_to_json_attribute(:login , 'created_by')
-
end
-
-
1
self.related_resources = [:orders]
-
-
# Quick fix for the warehouse
-
1
with_association(:order) do
-
1
with_association(:project) do
-
1
map_attribute_to_json_attribute(:uuid , 'project_uuid')
-
1
map_attribute_to_json_attribute(:id , 'project_internal_id')
-
1
map_attribute_to_json_attribute(:name , 'project_name')
-
end
-
-
1
with_association(:study) do
-
1
map_attribute_to_json_attribute(:uuid , 'study_uuid')
-
1
map_attribute_to_json_attribute(:id , 'study_internal_id')
-
1
map_attribute_to_json_attribute(:name , 'study_name')
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Api::TagIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::TagIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([ :uuid_object, { :tag_group => [:uuid_object] } ]) }
-
end
-
end
-
end
-
-
1
renders_model(::Tag)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:oligo, 'expected_sequence')
-
1
map_attribute_to_json_attribute(:map_id)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
with_association(:tag_group) do
-
1
map_attribute_to_json_attribute(:name, 'tag_group_name')
-
1
map_attribute_to_json_attribute(:uuid, 'tag_group_uuid')
-
1
map_attribute_to_json_attribute(:id , 'tag_group_internal_id')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Api::WellIO < Api::Base
-
1
module Extensions
-
1
module ClassMethods
-
1
def render_class
-
Api::WellIO
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
1
scope :including_associations_for_json, -> { includes([:uuid_object, :map, :well_attribute, :plate, { :primary_aliquot => { :sample => :uuid_object } } ])}
-
end
-
end
-
end
-
1
renders_model(::Well)
-
-
1
map_attribute_to_json_attribute(:uuid)
-
1
map_attribute_to_json_attribute(:id, 'internal_id')
-
1
map_attribute_to_json_attribute(:name)
-
1
map_attribute_to_json_attribute(:display_name)
-
1
map_attribute_to_json_attribute(:created_at)
-
1
map_attribute_to_json_attribute(:updated_at)
-
-
1
extra_json_attributes do |object, json_attributes|
-
sample = object.primary_aliquot_if_unique.try(:sample)
-
if sample.present?
-
json_attributes["genotyping_status"] = object.genotyping_status
-
json_attributes["genotyping_snp_plate_id"] = sample.genotyping_snp_plate_id
-
end
-
end
-
-
1
with_association(:well_attribute) do
-
1
map_attribute_to_json_attribute(:gel_pass, 'gel_pass')
-
1
map_attribute_to_json_attribute(:concentration, 'concentration')
-
1
map_attribute_to_json_attribute(:current_volume, 'current_volume')
-
1
map_attribute_to_json_attribute(:buffer_volume, 'buffer_volume')
-
1
map_attribute_to_json_attribute(:requested_volume, 'requested_volume')
-
1
map_attribute_to_json_attribute(:picked_volume, 'picked_volume')
-
1
map_attribute_to_json_attribute(:pico_pass, 'pico_pass')
-
1
map_attribute_to_json_attribute(:measured_volume, 'measured_volume')
-
1
map_attribute_to_json_attribute(:sequenom_count, 'sequenom_count')
-
1
map_attribute_to_json_attribute(:gender_markers_string, 'gender_markers')
-
end
-
-
1
with_association(:map) do
-
1
map_attribute_to_json_attribute(:description, 'map')
-
end
-
-
1
with_association(:plate) do
-
1
map_attribute_to_json_attribute(:barcode, 'plate_barcode')
-
1
map_attribute_to_json_attribute(:uuid, 'plate_uuid')
-
-
1
extra_json_attributes do |object, json_attributes|
-
json_attributes["plate_barcode_prefix"] = object.prefix unless object.nil?
-
end
-
end
-
-
1
with_association(:primary_aliquot_if_unique) do
-
1
with_association(:sample) do
-
1
map_attribute_to_json_attribute(:uuid, 'sample_uuid')
-
1
map_attribute_to_json_attribute(:id , 'sample_internal_id')
-
1
map_attribute_to_json_attribute(:name, 'sample_name')
-
end
-
end
-
-
1
self.related_resources = [ :lanes, :requests ]
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class ApiApplication < ActiveRecord::Base
-
-
1
validates_presence_of :name, :key, :contact, :privilege
-
-
1
validates_inclusion_of :privilege, :in => ['full','tag_plates']
-
-
1
validates_length_of :key, :minimum=>20
-
-
1
before_validation :generate_new_api_key, :unless => :key?
-
-
1
def generate_new_api_key
-
self.key = SecureRandom.base64(configatron.fetch('api_key_length')||20)
-
end
-
-
1
def generate_new_api_key!
-
generate_new_api_key
-
save!
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
require 'lib/eventful_record'
-
require 'lib/external_properties'
-
-
require 'lib/eventful_record'
-
require 'lib/external_properties'
-
-
class Asset < ActiveRecord::Base
-
include StudyReport::AssetDetails
-
include ModelExtensions::Asset
-
include AssetLink::Associations
-
-
SAMPLE_PARTIAL = 'assets/samples_partials/blank'
-
-
module InstanceMethods
-
# Assets are, by default, non-barcoded
-
def generate_barcode
-
# Does nothing!
-
end
-
-
# Returns nil because assets really don't have barcodes!
-
def barcode_type
-
nil
-
end
-
end
-
include InstanceMethods
-
-
class VolumeError< StandardError
-
end
-
-
def summary_hash
-
{
-
:asset_id => id
-
}
-
end
-
-
def sample_partial
-
self.class::SAMPLE_PARTIAL
-
end
-
-
self.per_page = 500
-
self.inheritance_column = "sti_type"
-
#acts_as_paranoid
-
# validates_uniqueness_of :name
-
-
has_many :asset_group_assets, :dependent => :destroy
-
has_many :asset_groups, :through => :asset_group_assets
-
has_many :asset_audits
-
-
# TODO: Remove 'requests' and 'source_request' as they are abiguous
-
has_many :requests
-
has_one :source_request, :class_name => "Request", :foreign_key => :target_asset_id, :include => :request_metadata
-
has_many :requests_as_source, :class_name => 'Request', :foreign_key => :asset_id, :include => :request_metadata
-
has_many :requests_as_target, :class_name => 'Request', :foreign_key => :target_asset_id, :include => :request_metadata
-
-
scope :include_requests_as_target, -> { includes(:requests_as_target) }
-
scope :include_requests_as_source, -> { includes(:requests_as_source) }
-
-
#Orders
-
has_many :submitted_assets
-
has_many :orders, :through => :submitted_assets
-
-
scope :requests_as_source_is_a?, ->(t) { { :joins => :requests_as_source, :conditions => { :requests => { :sti_type => [ t, *t.descendants ].map(&:name) } } } }
-
-
extend ContainerAssociation::Extension
-
-
# to override in subclass
-
def location
-
nil
-
end
-
-
belongs_to :map
-
belongs_to :barcode_prefix
-
scope :sorted , order("map_id ASC")
-
-
scope :position_name, ->(*args) {
-
joins(:map).where(["description = ? AND asset_size = ?", args[0], args[1]])
-
}
-
scope :get_by_type, ->(*args) { {:conditions => { :sti_type => args[0]} } }
-
scope :for_summary, -> { includes([:map,:barcode_prefix]) }
-
-
scope :of_type, ->(*args) { { :conditions => { :sti_type => args.map { |t| [t, *t.descendants] }.flatten.map(&:name) } } }
-
-
scope :recent_first, -> { order('id DESC') }
-
-
def studies
-
[]
-
end
-
-
def barcode_and_created_at_hash
-
return {} if barcode.blank?
-
{
-
:barcode => generate_machine_barcode,
-
:created_at => created_at
-
}
-
end
-
-
def study_ids
-
[]
-
end
-
-
# All studies related to this asset
-
def related_studies
-
(orders.map(&:study)+studies).compact.uniq
-
end
-
# Named scope for search by query string behaviour
-
scope :for_search_query, ->(query,with_includes) {
-
-
search = '(assets.sti_type != "Well") AND ((assets.name IS NOT NULL AND assets.name LIKE :name)'
-
arguments = {:name => "%#{query}%"}
-
-
# The entire string consists of one of more numeric characters, treat it as an id or barcode
-
if /\A\d+\z/ === query
-
search << ' OR (assets.id = :id) OR (assets.barcode = :barcode)'
-
arguments.merge!({:id => query.to_i, :barcode => query.to_s})
-
end
-
-
# If We're a Sanger Human barcode
-
if match = /\A([A-z]{2})(\d{1,7})[A-z]{0,1}\z/.match(query)
-
prefix_id = BarcodePrefix.find_by_prefix(match[1]).try(:id)
-
number = match[2]
-
search << ' OR (assets.barcode = :barcode AND assets.barcode_prefix_id = :prefix_id)' unless prefix_id.nil?
-
arguments.merge!({:barcode => number, :prefix_id => prefix_id})
-
end
-
-
search <<')'
-
-
{
-
:conditions => [ search, arguments ]
-
}.tap {|cond| cond.merge!(:include => :requests, :order => 'requests.pipeline_id ASC') if with_includes }
-
}
-
-
scope :with_name, ->(*names) { { :conditions => { :name => names.flatten } } }
-
-
extend EventfulRecord
-
has_many_events do
-
event_constructor(:create_external_release!, ExternalReleaseEvent, :create_for_asset!)
-
event_constructor(:create_pass!, Event::AssetSetQcStateEvent, :create_passed!)
-
event_constructor(:create_fail!, Event::AssetSetQcStateEvent, :create_failed!)
-
event_constructor(:create_scanned_into_lab!, Event::ScannedIntoLabEvent, :create_for_asset!)
-
event_constructor(:create_plate!, Event::PlateCreationEvent, :create_for_asset!)
-
event_constructor(:create_plate_with_date!, Event::PlateCreationEvent, :create_for_asset_with_date!)
-
event_constructor(:create_sequenom_stamp!, Event::PlateCreationEvent, :create_sequenom_stamp_for_asset!)
-
event_constructor(:create_sequenom_plate!, Event::PlateCreationEvent, :create_sequenom_plate_for_asset!)
-
event_constructor(:create_gel_qc!, Event::SampleLogisticsQcEvent, :create_gel_qc_for_asset!)
-
event_constructor(:create_pico!, Event::SampleLogisticsQcEvent, :create_pico_result_for_asset!)
-
event_constructor(:created_using_sample_manifest!, Event::SampleManifestEvent, :created_sample!)
-
event_constructor(:updated_using_sample_manifest!, Event::SampleManifestEvent, :updated_sample!)
-
event_constructor(:updated_fluidigm_plate!, Event::SequenomLoading, :updated_fluidigm_plate!)
-
event_constructor(:update_gender_markers!, Event::SequenomLoading, :created_update_gender_makers!)
-
event_constructor(:update_sequenom_count!, Event::SequenomLoading, :created_update_sequenom_count!)
-
end
-
has_many_lab_events
-
-
has_one_event_with_family 'scanned_into_lab'
-
has_one_event_with_family 'moved_to_2d_tube'
-
-
# Key/value stores and attributes
-
include ExternalProperties
-
acts_as_descriptable :serialized
-
include PolymorphicAttributable
-
include Uuid::Uuidable
-
-
# Links to other databases
-
include Identifiable
-
-
include Commentable
-
include Event::PlateEvents
-
-
#set_polymorphic_attributes :sample
-
-
# Returns the request options used to create this asset. By default assumed to be empty.
-
def created_with_request_options
-
{}
-
end
-
-
def is_sequenceable?
-
false
-
end
-
-
# Returns the type of asset that can be considered appropriate for request types.
-
def asset_type_for_request_types
-
self.class
-
end
-
-
def tube_name
-
(primary_aliquot.nil? or primary_aliquot.sample.sanger_sample_id.blank?) ? self.name : primary_aliquot.sample.shorten_sanger_sample_id
-
end
-
-
def study
-
studies.first
-
end
-
-
def study_id
-
study.try(:id)
-
end
-
-
def ancestor_of_purpose(ancestor_purpose_id)
-
# If it's not a tube or a plate, defaults to stock_plate
-
return self.stock_plate
-
end
-
-
has_one :creation_request, :class_name => 'Request', :foreign_key => :target_asset_id
-
-
def label
-
self.sti_type || 'Unknown'
-
end
-
-
def label=(new_type)
-
self.sti_type = new_type
-
end
-
-
def request_types
-
RequestType.find(:all, :conditions => {:asset_type => label})
-
end
-
-
def scanned_in_date
-
self.scanned_into_lab_event.try(:content) || ''
-
end
-
-
def moved_to_2D_tube_date
-
self.moved_to_2d_tube_event.try(:content) || ''
-
end
-
-
def create_asset_group_wells(user, params)
-
asset_group = AssetGroup.create(params)
-
asset_group.user = user
-
asset_group.assets = wells
-
asset_group.save!
-
-
# associate sample to study
-
if asset_group.study
-
wells.each do |well|
-
next unless well.sample
-
well.sample.studies<< asset_group.study
-
well.sample.save!
-
end
-
end
-
-
asset_group
-
-
end
-
-
after_create :generate_name_with_id, :if => :name_needs_to_be_generated?
-
-
def name_needs_to_be_generated?
-
@name_needs_to_be_generated
-
end
-
private :name_needs_to_be_generated?
-
-
def generate_name_with_id
-
self.update_attributes!(:name => "#{self.name} #{self.id}")
-
end
-
-
def generate_name(new_name)
-
self.name = new_name
-
@name_needs_to_be_generated = self.library_prep?
-
end
-
-
#todo unify with parent/children
-
def parent
-
self.parents.first
-
end
-
-
def child
-
self.children.last
-
end
-
-
# Labware reflects the physical piece of plastic corresponding to an asset
-
def labware
-
self
-
end
-
-
def library_prep?
-
false
-
end
-
-
def display_name
-
self.name.blank? ? "#{self.sti_type} #{self.id}" : self.name
-
end
-
-
def external_identifier
-
"#{self.sti_type}#{self.id}"
-
end
-
-
def details
-
nil
-
end
-
-
QC_STATES = [
-
[ 'passed', 'pass' ],
-
[ 'failed', 'fail' ],
-
[ 'pending', 'pending' ],
-
[ nil, '']
-
]
-
-
QC_STATES.reject { |k,v| k.nil? }.each do |state, qc_state|
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def qc_#{qc_state}
-
self.qc_state = #{state.inspect}
-
self.save!
-
end
-
}, __FILE__, line)
-
end
-
-
1
def compatible_qc_state
-
QC_STATES.assoc(qc_state).try(:last) || qc_state
-
end
-
-
1
def set_qc_state(state)
-
self.qc_state = QC_STATES.rassoc(state).try(:first) || state
-
self.save
-
self.set_external_release(self.qc_state)
-
end
-
-
1
def has_been_through_qc?
-
not self.qc_state.blank?
-
end
-
-
1
def set_external_release(state)
-
update_external_release do
-
case
-
when state == 'failed' then self.external_release = false
-
when state == 'passed' then self.external_release = true
-
when state == 'pending' then self # Do nothing
-
when state.nil? then self # TODO: Ignore for the moment, correct later
-
when [ 'scanned_into_lab' ].include?(state.to_s) then self # TODO: Ignore for the moment, correct later
-
else raise StandardError, "Invalid external release state #{state.inspect}"
-
end
-
end
-
end
-
-
1
def update_external_release(&block)
-
external_release_nil_before = external_release.nil?
-
yield
-
self.save!
-
self.events.create_external_release!(!external_release_nil_before) unless self.external_release.nil?
-
end
-
1
private :update_external_release
-
-
1
def self.find_by_human_barcode(barcode, location)
-
data = Barcode.split_human_barcode(barcode)
-
if data[0] == 'DN'
-
plate = Plate.find_by_barcode(data[1])
-
well = plate.find_well_by_name(location)
-
return well if well
-
end
-
raise ActiveRecord::RecordNotFound, "Couldn't find well with for #{barcode} #{location}"
-
end
-
-
1
def assign_relationships(parents, child)
-
if parents.kind_of?(Array) && child.kind_of?(Asset)
-
parents.each do |parent|
-
parent.children.delete(child)
-
end
-
-
AssetLink.create_edge(self, child)
-
-
parents.each do |parent|
-
AssetLink.create_edge(parent, self)
-
end
-
end
-
end
-
-
1
def self.print_assets(assets, barcode_printer)
-
printables = []
-
assets.each do |asset|
-
printables.push PrintBarcode::Label.new({ :number => asset.barcode, :study => asset.tube_name, :suffix => "", :prefix => asset.prefix })
-
end
-
begin
-
unless printables.empty?
-
barcode_printer.print_labels(printables)
-
end
-
rescue
-
return false
-
end
-
-
true
-
end
-
-
# We accept not only an individual barcode but also an array of them. This builds an appropriate
-
# set of conditions that can find any one of these barcodes. We map each of the individual barcodes
-
# to their appropriate query conditions (as though they operated on their own) and then we join
-
# them together with 'OR' to get the overall conditions.
-
1
scope :with_machine_barcode, ->(*barcodes) {
-
query_details = barcodes.flatten.map do |source_barcode|
-
case source_barcode.to_s
-
when /^\d{13}$/ #An EAN13 barcode
-
barcode_number = Barcode.number_to_human(source_barcode)
-
prefix_string = Barcode.prefix_from_barcode(source_barcode)
-
barcode_prefix = BarcodePrefix.find_by_prefix(prefix_string)
-
-
if barcode_number.nil? or prefix_string.nil? or barcode_prefix.nil?
-
{ :query => 'FALSE' }
-
else
-
{ :query => '(barcode=? AND barcode_prefix_id=?)', :conditions => [ barcode_number, barcode_prefix.id ] }
-
end
-
when /^\d{10}$/ # A Fluidigm barcode
-
{ :joins => 'JOIN plate_metadata AS pmmb ON pmmb.plate_id = assets.id', :query=>'(pmmb.fluidigm_barcode=?)', :conditions => source_barcode.to_s }
-
else
-
{ :query => 'FALSE' }
-
end
-
end.inject({ :query => ['FALSE'], :conditions => [nil], :joins=>[] }) do |building, current|
-
building.tap do
-
building[:joins] << current[:joins]
-
building[:query] << current[:query]
-
building[:conditions] << current[:conditions]
-
end
-
end
-
-
{
-
:conditions => [ query_details[:query].join(' OR '), *query_details[:conditions].flatten.compact ],
-
:joins => query_details[:joins].compact.uniq
-
}
-
}
-
-
-
1
scope :source_assets_from_machine_barcode, ->(destination_barcode) {
-
destination_asset = find_from_machine_barcode(destination_barcode)
-
if destination_asset
-
source_asset_ids = destination_asset.parents.map(&:id)
-
unless source_asset_ids.empty?
-
{ :conditions => ["id IN (?)",source_asset_ids ] }
-
else
-
{ :conditions => 'FALSE' }
-
end
-
else
-
{ :conditions => 'FALSE' }
-
end
-
}
-
-
-
1
def self.find_from_machine_barcode(source_barcode)
-
with_machine_barcode(source_barcode).first
-
end
-
-
1
def generate_machine_barcode
-
"#{Barcode.calculate_barcode( barcode_prefix.prefix,barcode.to_i)}"
-
end
-
-
1
def external_release_text
-
return "Unknown" if self.external_release.nil?
-
return self.external_release? ? "Yes" : "No"
-
end
-
-
1
def add_parent(parent)
-
return unless parent
-
#should be self.parents << parent but that doesn't work
-
-
self.save!
-
parent.save!
-
AssetLink.create_edge!(parent, self)
-
end
-
-
1
def attach_tag(tag)
-
tag.tag!(self) if tag.present?
-
end
-
-
1
def requests_status(request_type)
-
# get the most recent request (ignore previous runs)
-
self.requests.sort_by{ |r| r.id }.select{ |request| request.request_type == request_type }.map{ |filtered_request| filtered_request.state }
-
end
-
-
1
def transfer(max_transfer_volume)
-
-
transfer_volume = [max_transfer_volume.to_f, self.volume || 0.0].min
-
raise VolumeError, "not enough volume left" if transfer_volume <=0
-
-
self.class.create!(:name => self.name) do |new_asset|
-
new_asset.aliquots = self.aliquots.map(&:dup)
-
new_asset.volume = transfer_volume
-
update_attributes!(:volume => self.volume - transfer_volume) #Â Update ourselves
-
end.tap do |new_asset|
-
new_asset.add_parent(self)
-
end
-
end
-
-
1
def spiked_in_buffer
-
return nil
-
end
-
-
1
def has_stock_asset?
-
return false
-
end
-
-
-
1
def has_many_requests?
-
Request.find_all_target_asset(self.id).size > 1
-
end
-
-
1
def is_a_resource
-
self.resource == true
-
end
-
-
1
def can_be_created?
-
false
-
end
-
-
1
def compatible_purposes
-
[]
-
end
-
-
1
def automatic_move?
-
false
-
end
-
-
# See Aliquot::Receptacle for handling of assets with contents
-
1
def tag_count
-
nil
-
end
-
-
# We only support wells for the time being
-
1
def latest_stock_metric(product,*args)
-
nil
-
end
-
-
1
def contained_samples; []; end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
-
1
module Asset::Ownership
-
-
1
module ChangesOwner
-
-
# Included in events which change ownership of plates
-
1
def self.included(base)
-
5
base.class_eval do
-
5
extend ClassMethods
-
5
after_create :assign_owner
-
5
has_many :owners, :as => :eventable
-
end
-
end
-
-
1
def assign_owner
-
[target_for_ownership].flatten.map { |target| target.change_owner_to(user,self) }
-
end
-
1
private :assign_owner
-
-
1
module ClassMethods
-
1
def set_target_for_owner(target)
-
4
alias_method(:target_for_ownership, target)
-
end
-
end
-
end
-
-
1
module Unowned
-
1
def change_owner_to(owner,source_event)
-
# Do nothing
-
end
-
end
-
-
1
module Owned
-
# Currently only plates can be owned.
-
-
1
def self.included(base)
-
1
base.class_eval do
-
-
1
has_one :plate_owner
-
1
has_one :owner, :source => :user, :through => :plate_owner
-
-
1
scope :for_user, ->(user) {
-
joins(:plate_owner).
-
where(:plate_owners => {:user_id => user })
-
}
-
-
end
-
end
-
-
1
def change_owner_to(owner,source_event)
-
if plate_owner.nil?
-
self.update_attributes!(:plate_owner=>PlateOwner.create!(:user => owner, :eventable => source_event, :plate => self))
-
else
-
plate_owner.update_attributes!(:user => owner, :eventable => source_event)
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014 Genome Research Ltd.
-
module Asset::Stock
-
# Extending this module will allow an asset to have a stock asset and be able to
-
# create it.
-
module CanCreateStockAsset
-
def self.extended(base)
-
base.class_eval do
-
has_one_as_child(:stock_asset, :conditions=> {:sti_type => stock_asset_type.name} )
-
-
stock_asset_factory(:create_stock_asset!, :create!)
-
stock_asset_factory(:new_stock_asset, :new)
-
deprecate :new_stock_asset
-
1
-
delegate :is_a_stock_asset?, :to => 'self.class'
-
end
-
end
-
-
# By being able to create a stock asset the asset itself is not a stock.
-
def is_a_stock_asset?
-
false
-
end
-
-
def stock_asset_factory(name, ctor)
-
line = __LINE__
-
class_eval(%Q{
-
def #{name}(attributes = {}, &block)
-
self.class.stock_asset_type.#{ctor}(attributes.reverse_merge(
-
:name => "(s) \#{self.name}",
-
:barcode => AssetBarcode.new_barcode,
-
:aliquots => self.aliquots.map(&:dup),
-
:purpose => self.class.stock_asset_purpose
-
), &block)
-
end
-
}, __FILE__, line)
-
end
-
end
-
-
def has_stock_asset?
-
false
-
end
-
-
def is_a_stock_asset?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
class AssetAudit < ActiveRecord::Base
-
1
include Api::AssetAuditIO::Extensions
-
1
include Uuid::Uuidable
-
1
include ::Io::AssetAudit::ApiIoSupport
-
-
1
belongs_to :asset
-
-
-
1
self.per_page = 500
-
-
1
validates_presence_of :asset, :key
-
1
validates_format_of :key, :with => /^[\w_]+$/i, :message => I18n.t('asset_audit.key_format'), :on => :create
-
-
# Disabled in the initial events release. One enabling ensure historical audits
-
# get broadcast
-
# after_create :broadcast_event
-
-
1
private
-
-
1
def broadcast_event
-
BroadcastEvent::AssetAudit.create!(:seed=>self,:user=>User.find_by_login(created_by),:created_at=>created_at)
-
end
-
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
class AssetBarcode < ActiveRecord::Base
-
# This class only a concurrency safe counter to generate asset barcode
-
1
def self.new_barcode
-
barcode = (AssetBarcode.create!).id
-
-
while Asset.find_by_barcode(barcode.to_s)
-
barcode = (AssetBarcode.create!).id
-
end
-
-
(barcode).to_s
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
class AssetCreation < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include Asset::Ownership::ChangesOwner
-
1
extend ModelExtensions::Plate::NamedScopeHelpers
-
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
1
validates_presence_of :parent
-
-
1
def parent_nil?
-
parent.nil?
-
end
-
1
private :parent_nil?
-
-
1
belongs_to :child_purpose, :class_name => 'Purpose'
-
1
validates :child_purpose, :presence => true, :unless => :multiple_purposes
-
1
validates_each(:child_purpose, :unless => :parent_nil?, :allow_blank => true) do |record, attr, child_purpose|
-
record.errors.add(:child_purpose, 'is not a valid child type') unless record.parent.purpose.child_purposes.include?(child_purpose)
-
end
-
-
1
before_create :process_children
-
1
def process_children
-
create_children!
-
connect_parent_and_children
-
record_creation_of_children
-
end
-
1
private :process_children
-
-
1
def create_ancestor_asset!(asset, child)
-
AssetLink.create_edge!(asset, child)
-
end
-
-
1
def can_create_ancestor_plate?(asset, child)
-
(asset.kind_of? Well) && (!child.nil?) && (!child.ancestors.include?(asset))
-
end
-
-
1
def connect_parent_and_children
-
children.each {|child| create_ancestor_asset!(parent, child)}
-
end
-
1
private :connect_parent_and_children
-
-
1
def multiple_purposes
-
false
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class AssetGroup < ActiveRecord::Base
-
-
1
include Uuid::Uuidable
-
1
include ModelExtensions::AssetGroup
-
-
1
belongs_to :study
-
1
belongs_to :user
-
1
belongs_to :submission # Optional, present if created by a particular submission
-
-
1
has_many :asset_group_assets
-
1
has_many :assets, :through => :asset_group_assets
-
-
1
validates :name, :presence => true, :uniqueness => true
-
1
validates :study, :presence => true
-
-
1
scope :for_search_query, ->(query,with_includes) { where([ 'name LIKE ?', "%#{query}%" ]) }
-
-
1
def all_samples_have_accession_numbers?
-
unaccessioned_samples.count == 0
-
end
-
-
1
def unaccessioned_samples
-
Sample.find(:all,
-
:joins => [
-
'INNER JOIN aliquots ON aliquots.sample_id = samples.id',
-
'INNER JOIN sample_metadata ON sample_metadata.sample_id = samples.id'
-
],
-
:conditions => ['aliquots.receptacle_id IN (?) AND sample_ebi_accession_number IS NULL',assets.map(&:id)]
-
)
-
end
-
-
1
def self.find_or_create_asset_group(new_assets_name, study)
-
# Is new name set or create group
-
asset_group = nil
-
if ! new_assets_name.empty?
-
asset_group = AssetGroup.find(:first,:conditions => [" name = ? ", new_assets_name ])
-
if asset_group.nil?
-
#create new asset group
-
asset_group = AssetGroup.create(:name => new_assets_name, :study => study)
-
asset_group.save
-
end
-
end
-
return asset_group
-
end
-
-
1
def automatic_move?
-
asset_types.one? && assets.first.automatic_move?
-
end
-
-
1
def asset_types
-
assets.map(&:sti_type).uniq
-
end
-
-
1
def duplicate(project)
-
# TODO: Implement me
-
end
-
-
1
def move(assets)
-
# TODO: Implement me
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class AssetGroupAsset < ActiveRecord::Base
-
1
belongs_to :asset
-
1
belongs_to :asset_group
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
-
class AssetLink < ActiveRecord::Base
-
include Api::AssetLinkIO::Extensions
-
-
acts_as_dag_links :node_class_name => 'Asset'
-
-
# Enables the bulk creation of the asset links defined by the pairs passed as edges.
-
# Basically we should be moving away from these and this enables us to ignore them.
-
class BuilderJob < Struct.new(:links)
-
def perform
-
ActiveRecord::Base.transaction do
-
links.map { |parent,child| AssetLink.create_edge(Asset.find(parent),Asset.find(child)) }
-
end
-
end
-
-
def self.create(*args)
-
Delayed::Job.enqueue(new(*args))
-
end
-
end
-
-
# Convenient mechanism for queueing the creation of AssetLink instances where there is
-
# singular parent with lots of children.
-
class Job < BuilderJob
-
def initialize(parent, children)
-
super(children.map { |child| [parent.id,child.id] })
-
end
-
end
-
-
-
self.per_page = 500
-
include Uuid::Uuidable
-
-
def destroy!
-
end
-
-
module Associations
-
def self.included(base)
-
base.class_eval do
-
extend ClassMethods
-
-
has_dag_links :link_class_name => 'AssetLink'
-
end
-
base.extend(ClassMethods)
-
end
-
-
module ClassMethods
-
def has_one_as_child(name, options = {})
-
# has_one(name, options.merge(:through => :links_as_child, :source => :ancestor))
-
-
line = __LINE__ + 1
-
class_eval(%Q{
-
-
1
def #{name}
-
ancestors.find(:first,#{options.inspect})
-
end
-
-
1
def #{name}=(value)
-
raise RuntimeError, 'Value for #{name} must be saved' if value.new_record?
-
old_value = self.#{name}
-
parents.delete(old_value) if old_value.present?
-
AssetLink.create_edge!(value, self)
-
end
-
-
1
def has_#{name}?
-
#{name}.present?
-
end
-
}, __FILE__, line)
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
class AssetRack < Asset
-
-
include LocationAssociation::Locatable
-
include Transfer::Associations
-
include Barcode::Barcodeable
-
include Asset::Ownership::Unowned
-
extend QcFile::Associations
-
has_qc_files
-
-
# Yuck! Yuck! Yuck!
-
self.prefix = "DN"
-
-
scope :include_asset_rack_purpose, ->{ includes(:purpose) }
-
belongs_to :purpose, :class_name => 'AssetRack::Purpose', :foreign_key => :plate_purpose_id
-
1
alias_method :asset_rack_purpose, :purpose
-
-
delegate :default_state, :barcode_type, :asset_shape, :source_plate_purpose, :well_maps, :to => :purpose, :allow_nil => true
-
-
1
contains :strip_tubes do
-
-
1
def construct!(source=nil)
-
-
source ||= proxy_association.owner.source_plate||proxy_association.owner
-
-
strips = proxy_association.owner.maps.map do |map|
-
{
-
:name => "#{source.sanger_human_barcode}:#{map.description}",
-
:map => map
-
}
-
end
-
proxy_association.owner.strip_tubes.build(strips)
-
proxy_association.owner.save!
-
-
end
-
-
end
-
-
1
def wells
-
AssetRack::WellAssociations::WellProxy.new(self)
-
end
-
-
1
def ancestor_of_purpose(purpose)
-
self.ancestors.first(:order => 'created_at DESC', :conditions => {:plate_purpose_id=>purpose})
-
end
-
-
1
def ancestors_of_purpose(purpose)
-
self.ancestors.find(:all,:order => 'created_at DESC', :conditions => {:plate_purpose_id=>purpose})
-
end
-
-
1
def lookup_stock_plate
-
ancestor_of_purpose(PlatePurpose.find(:all,:conditions=>{:can_be_considered_a_stock_plate=>true}))
-
end
-
1
private :lookup_stock_plate
-
-
# AssetRacks won't be stock plates any time soon
-
1
alias_method :stock_plate, :lookup_stock_plate
-
-
1
def source_plate
-
ancestor_of_purpose(source_plate_purpose)
-
end
-
-
1
def source_plates
-
ancestors_of_purpose(source_plate_purpose)
-
end
-
-
1
def supports_multiple_submissions?
-
false
-
end
-
-
1
def priority
-
Submission.find(:first,
-
:select => 'MAX(submissions.priority) AS priority',
-
:joins => [
-
'INNER JOIN requests as reqp ON reqp.submission_id = submissions.id',
-
'INNER JOIN container_associations AS caplp ON caplp.content_id = reqp.target_asset_id'
-
],
-
:conditions => ['caplp.container_id IN (?)', strip_tubes.map(&:id)]
-
).try(:priority)||0
-
end
-
-
1
def self.create_with_barcode!(*args, &block)
-
attributes = args.extract_options!
-
barcode = args.first || attributes[:barcode]
-
barcode = nil if barcode.present? and find_by_barcode(barcode).present?
-
barcode ||= PlateBarcode.create.barcode
-
create!(attributes.merge(:barcode => barcode), &block)
-
end
-
-
1
def valid_positions?(positions)
-
unique_positions_on_plate = well_maps.where_description(positions).all.map(&:description).sort.uniq
-
unique_positions_from_caller = positions.sort.uniq
-
unique_positions_on_plate == unique_positions_from_caller
-
end
-
-
# Transfer requests into a rack are the requests leading into the wells of said rack.
-
1
def transfer_requests
-
wells.all(:include => :transfer_requests_as_target).map(&:transfer_requests_as_target).flatten
-
end
-
-
1
def transfer_request_type_from(source)
-
purpose.transfer_request_type_from(source.plate_purpose)
-
end
-
-
1
class Purpose < ::Purpose
-
-
1
has_many :asset_racks, :foreign_key => :plate_purpose_id, :inverse_of => :purpose
-
1
belongs_to :asset_shape, :class_name => 'AssetShape'
-
-
1
def source_plate_purpose
-
::Purpose.find_by_name!('Cherrypicked')
-
end
-
-
-
1
def create!(*args, &block)
-
attributes = args.extract_options!
-
do_not_create_strips = !!args.first
-
-
source = attributes.delete(:source)
-
-
attributes[:size] ||= size
-
attributes[:location] ||= default_location
-
attributes[:purpose] = self
-
-
target_type.constantize.create_with_barcode!(attributes, &block).tap do |plate|
-
plate.strip_tubes.construct!(source) unless do_not_create_strips
-
end
-
end
-
-
##
-
# We're fixed to a standard 96 well plate map for the moment.
-
1
def well_maps
-
Map.where_plate_size(self.size*strip_size).where_plate_shape(AssetShape.default)
-
end
-
-
1
def strip_size
-
8
-
end
-
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module AssetRack::WellAssociations
-
-
1
class WellProxy
-
-
1
attr_reader :asset_rack
-
-
1
def initialize(rack)
-
@asset_rack = rack
-
end
-
-
1
def well_scope
-
Well.for_asset_rack(asset_rack)
-
end
-
-
1
def method_missing(method,*params,&block)
-
well_scope.send(method,*params,&block)
-
end
-
-
1
def strip_tubes_in_columns
-
@stic ||= Hash[asset_rack.strip_tubes.map {|st| [st.map.column, st.id] }]
-
end
-
-
##
-
# We make a few assumptions about the layout of the asset rack and its children here.
-
# This may need to be revisited in future
-
1
def located_at(locations)
-
Well.for_strip_tubes_row(column_wells_for(locations).map do |column,rows|
-
[strip_tubes_in_columns[column],column,rows]
-
end)
-
end
-
-
1
alias_method :located_at_position, :located_at
-
-
##
-
# Assumes a fixed 12xcolumn-wise strip layout. Will need to delegate and
-
# Refactor if things change in future
-
1
def column_wells_for(locations)
-
Hash.new {|hash,column| hash[column] = Array.new }. tap do |column_wells|
-
Array.wrap(locations).each do |location|
-
row, column = /^([A-Z])([0-9]+)$/.match(location).captures
-
column_wells[column.to_i-1] << row.getbyte(0)-65
-
end
-
end
-
end
-
-
end
-
-
1
module AssetRackAssociation
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :for_asset_rack, lambda{ |rack| {:select=>'assets.*',:joins=>:container_association,:conditions=>{:container_associations=>{:container_id=>rack.strip_tubes }} }}
-
-
##
-
# Quite specialised scope. Takes array of:
-
# [strip_id,strip_colum,row_offset]
-
# Strip column is used to provide a quick conversion back to the standard map description
-
# This is a performance optimization
-
1
scope :for_strip_tubes_row, lambda{|strips_wells|
-
-
query = 'false'
-
conds = [query]
-
id_column = Array.new
-
strips_wells.each do |strip_id,column_no,rows|
-
query << ' OR (fstr_ca.container_id=? AND fstr_map.column_order IN (?))'
-
conds.concat([strip_id,rows])
-
id_column[column_no] = strip_id
-
end
-
-
# Selects a row by performing a simple CHAR conversion on the row order
-
# Selects column by finding the index of the strip_tube id in an array based on column
-
# Sadly rails doesn't automatically sanitize selects. We're probably safe with this one,
-
# but this ensures we can re-use this without worry.
-
select = sanitize_sql_array(['assets.*, CONCAT(CHAR(fstr_map.column_order+65),FIELD(fstr_ca.container_id,?)) AS map_description',id_column])
-
{
-
:select=>select,
-
:joins=>[
-
'INNER JOIN container_associations AS fstr_ca ON fstr_ca.content_id = assets.id',
-
'INNER JOIN maps AS fstr_map ON fstr_map.id = assets.map_id'
-
],
-
:conditions=>conds
-
}
-
}
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
# Creating an instance of this class causes a child plate, with the specified plate type, to be created from
-
# the parent.
-
1
class AssetRackCreation < AssetCreation
-
1
include_plate_named_scope :parent
-
-
# This is the child that is created from the parent. It cannot be assigned before validation.
-
1
belongs_to :parent, :class_name => 'Asset'
-
-
1
scope :include_child, -> { includes(:child) }
-
-
1
def record_creation_of_children
-
parent.events.create_plate!(child_purpose, child, user)
-
end
-
1
private :record_creation_of_children
-
-
1
module Children
-
-
1
def self.included(base)
-
1
base.class_eval %Q{
-
-
belongs_to :child, :class_name => 'AssetRack'
-
-
validates_unassigned(:child)
-
}
-
end
-
-
1
def target_for_ownership
-
child
-
end
-
1
private :target_for_ownership
-
-
1
def children
-
[self.child]
-
end
-
1
private :children
-
-
1
def create_children!
-
self.child = child_purpose.create!(:location=>parent.location,:source=>parent.source_plate)
-
end
-
1
private :create_children!
-
-
end
-
1
include Children
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class AssetShape < ActiveRecord::Base
-
-
1
validates_presence_of :name, :horizontal_ratio, :vertical_ratio, :description_strategy
-
1
validates_numericality_of :horizontal_ratio, :vertical_ratio
-
-
1
def self.default_id
-
AssetShape.find_by_name('Standard').id
-
end
-
-
1
def self.default
-
AssetShape.find_by_name('Standard')
-
end
-
-
1
def standard?
-
horizontal_ratio == 3 && vertical_ratio == 2
-
end
-
-
1
def multiplier(size)
-
((size/(vertical_ratio*horizontal_ratio))**0.5).to_i
-
end
-
1
private :multiplier
-
-
1
def plate_height(size)
-
multiplier(size)*vertical_ratio
-
end
-
-
1
def plate_width(size)
-
multiplier(size)*horizontal_ratio
-
end
-
-
1
def horizontal_to_vertical(well_position,plate_size)
-
alternate_position(well_position, plate_size, :width, :height)
-
end
-
-
1
def vertical_to_horizontal(well_position,plate_size)
-
alternate_position(well_position, plate_size, :height, :width)
-
end
-
-
1
def interlaced_vertical_to_horizontal(well_position,plate_size)
-
alternate_position(interlace(well_position,plate_size), plate_size, :height, :width)
-
end
-
-
1
def vertical_to_interlaced_vertical(well_position,plate_size)
-
interlace(well_position,plate_size)
-
end
-
-
1
def interlace(i,size)
-
m,d = (i-1).divmod(size/2)
-
2*d+1+m
-
end
-
1
private :interlace
-
-
1
def alternate_position(well_position, size, *dimensions)
-
return nil unless Map.valid_well_position?(well_position)
-
divisor, multiplier = dimensions.map { |n| send("plate_#{n}", size) }
-
column, row = (well_position-1).divmod(divisor)
-
return nil unless (0...multiplier).include?(column)
-
return nil unless (0...divisor).include?(row)
-
alternate = (row * multiplier) + column + 1
-
end
-
1
private :alternate_position
-
-
1
def location_from_row_and_column(row, column, size=96)
-
description_strategy.constantize.location_from_row_and_column(row, column,plate_width(size),size)
-
end
-
-
1
def location_from_index(index, size=96)
-
description_strategy.constantize.location_from_index(index,size)
-
end
-
-
-
1
def generate_map(size)
-
raise StandardError, 'Map already exists' if Map.find_by_asset_size_and_asset_shape_id(size,id).present?
-
ActiveRecord::Base.transaction do
-
(0...size).each do |i|
-
Map.create!(
-
:asset_size => size,
-
:asset_shape_id => self.id,
-
:location_id => i+1,
-
:row_order => i,
-
:column_order => horizontal_to_vertical(i,size)||0,
-
:description => location_from_index(i,size)
-
)
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class AssignTagsTask < Task
-
-
1
def included_for_render_task
-
[{:requests=>[{:asset=>[:asset_groups,{:primary_aliquot=>:sample}]},:target_asset,:batch_request]}, :pipeline]
-
end
-
-
1
class AssignTagsData < Task::RenderElement
-
1
alias_attribute :well, :asset
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && AssignTagsData.new(request)
-
end
-
-
1
def partial
-
"assign_tags_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_assign_tags_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_assign_tags_task(self, params)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class AssignTagsToTubesTask < AssignTagsTask
-
1
def do_task(workflow, params)
-
workflow.do_assign_tags_to_destination_task(self, params)
-
end
-
-
1
def included_for_render_task
-
[{:requests=>[{:asset=>[:map,:asset_groups,{:primary_aliquot=>:sample}]},:target_asset,:batch_request]}, :pipeline]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
1
class AssignTagsToWellsTask < Task
-
1
include Request::GroupingHelpers
-
-
1
class AssignTagsToWellsData < Task::RenderElement
-
1
alias_attribute :well, :asset
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && AssignTagsToWellsData.new(request)
-
end
-
-
1
def partial
-
"assign_tags_to_wells_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_assign_tags_to_wells_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_assign_tags_to_wells_task(self, params)
-
end
-
-
1
def assign_tags_to_wells(requests, well_id_tag_id_map)
-
# If the tubes have been processed they will have aliquots. That means that this is a retagging
-
# operation, otherwise we're initially tagging.
-
have_tagged_tubes_already = requests.map(&:target_asset).uniq.all? { |tube| not tube.aliquots.empty? }
-
have_tagged_tubes_already ? retag_tubes(requests, well_id_tag_id_map) : tag_tubes(requests, well_id_tag_id_map)
-
end
-
-
1
def retag_tubes(requests, well_id_tag_id_map)
-
# The first thing we do is build a graph of the transfers that have been made so that we can
-
# clean it out. The assets themselves cannot be rebuilt as people may be relying on the assets
-
# remaining consistent.
-
source_well_to_intermediate_wells, requests_to_destroy = {}, []
-
requests.each do |request|
-
source_well, target_tube = request.asset, request.target_asset
-
-
source_to_library = source_well.requests_as_source.where_is_a?(TransferRequest).first
-
library_well = source_to_library.target_asset
-
-
library_to_tagged = library_well.requests_as_source.where_is_a?(TransferRequest).first
-
tagged_well = library_to_tagged.target_asset
-
-
tagged_to_pooled = tagged_well.requests_as_source.where_is_a?(TransferRequest).first
-
pooled_well = tagged_to_pooled.target_asset
-
-
requests_to_destroy.concat([ source_to_library, library_to_tagged, tagged_to_pooled ])
-
requests_to_destroy.concat(pooled_well.requests_as_source.where_is_a?(TransferRequest).all)
-
-
source_well_to_intermediate_wells[source_well] = [library_well, tagged_well, pooled_well, target_tube]
-
end
-
requests_to_destroy.uniq.map(&:destroy)
-
-
# Now we can clean out the aliquots of all of the various assets and rebuild the graph so that the
-
# correct tags are in place.
-
pooled_well_to_tube = {}
-
source_well_to_intermediate_wells.values.flatten.uniq.each { |well| well.aliquots.clear }
-
source_well_to_intermediate_wells.each do |source_well, assets|
-
library_well, tagged_well, pooled_well, tube = assets
-
-
RequestType.transfer.create!(:asset => source_well, :target_asset => library_well, :state => 'passed')
-
library_well.aliquots.each { |aliquot| aliquot.update_attributes!(:library => library_well) }
-
-
RequestType.transfer.create!(:asset => library_well, :target_asset => tagged_well, :state => 'passed')
-
tag_id = well_id_tag_id_map[source_well.id]
-
Tag.find(tag_id).tag!(tagged_well) if tag_id.present?
-
-
RequestType.transfer.create!(:asset => tagged_well, :target_asset => pooled_well, :state => 'passed')
-
-
raise StandardError, "Pooled well into different tube!" unless tube == (pooled_well_to_tube[pooled_well] || tube)
-
pooled_well_to_tube[pooled_well] = tube
-
end
-
-
pooled_well_to_tube.each { |well, tube| RequestType.transfer.create!(:asset => well, :target_asset => tube) }
-
end
-
1
private :retag_tubes
-
-
1
def tag_tubes(requests, well_id_tag_id_map)
-
# to be compliant with the new pulldown application we have to create intermediate plate and wells
-
source_plates = requests.map(&:asset).map(&:plate).uniq
-
raise StandardError, "Can only tag based on one source plate" unless source_plates.size == 1
-
source_plate = source_plates.first
-
-
well_to_tagged = {}
-
tube_to_pool = {}
-
-
pooled_plate = Plate.create!(:size => source_plate.size)
-
library_plate = Plate.create!(:size => source_plate.size)
-
tag_plate = Plate.create!(:size => source_plate.size)
-
-
source_plate.wells.each do |well|
-
library_well = Well.create!
-
RequestType.transfer.create!(:asset => well, :target_asset => library_well, :state => 'passed')
-
library_plate.add_well_by_map_description(library_well, well.map_description)
-
library_well.aliquots.each { |aliquot| aliquot.update_attributes!(:library => library_well) }
-
-
tagged_well = Well.create!
-
well_to_tagged[well] =tagged_well
-
RequestType.transfer.create!(:asset => library_well, :target_asset => tagged_well, :state => 'passed')
-
tag_plate.add_well_by_map_description(tagged_well, well.map_description)
-
tag_id=well_id_tag_id_map[well.id]
-
Tag.find(tag_id).tag!(tagged_well) if tag_id
-
end
-
[library_plate, tag_plate].map(&:save!)
-
-
# We could be retagging because someone has changed their minds.
-
requests.each { |request| request.target_asset.aliquots.clear }
-
-
requests.each do |r|
-
tagged_well = well_to_tagged[r.asset]
-
raise "Well not tagged" if tagged_well.nil?
-
tube = r.target_asset
-
-
pooled_well = tube_to_pool[tube]
-
unless pooled_well
-
pooled_well = Well.create!
-
tube_to_pool[tube] = pooled_well
-
pooled_plate.add_well_by_map_description(pooled_well, tagged_well.map_description)
-
end
-
-
RequestType.transfer.create!(:asset => tagged_well, :target_asset => pooled_well, :state => 'passed')
-
# transfer between pooled_well and tube needs to be at the end, when all the aliquots are present
-
#RequestType.transfer.create!(:asset => pooled_well, :target_asset => tube)
-
end
-
-
tube_to_pool.each do |tube, pooled_well|
-
RequestType.transfer.create!(:asset => pooled_well, :target_asset => tube, :state => 'passed')
-
end
-
-
link_pulldown_indexed_libraries_to_multiplexed_library(requests)
-
end
-
1
private :tag_tubes
-
-
1
def validate_returned_tags_are_not_repeated_in_submission!(requests, params)
-
submission_to_tag = params[:tag].map do |well_id, tag_id|
-
well_requests = requests.select{|request| request.asset_id == well_id.to_i}
-
raise "couldnt find matching well request" if well_requests.empty? || well_requests.first.nil?
-
[well_requests.first.submission_id, tag_id]
-
end
-
raise "Duplicate tags in single multiplex" if submission_to_tag != submission_to_tag.uniq
-
-
nil
-
end
-
-
-
1
def create_tag_instances_and_link_to_wells(requests, params)
-
params[:tag].map do |well_id, tag_id|
-
ActiveRecord::Base.transaction do
-
Tag.find(tag_id).tag!(Well.find(well_id))
-
end
-
end
-
end
-
-
1
def find_sequencing_requests(pulldown_requests)
-
Request.find_all_by_submission_id(pulldown_requests.first.submission_id).select{ |sequencing_request| sequencing_request.is_a?(SequencingRequest) }
-
end
-
-
1
def link_pulldown_indexed_libraries_to_multiplexed_library(requests)
-
group_requests_by_submission_id(requests).each do |requests_with_same_submission|
-
sequencing_requests = find_sequencing_requests(requests_with_same_submission)
-
raise 'Couldnt find sequencing request' if sequencing_requests.empty?
-
-
# If the requests don't all end in the same tube!
-
raise 'Borked!' unless requests_with_same_submission.map(&:target_asset).compact.uniq.size == 1
-
sequencing_requests.each { |sequencing_request| sequencing_request.update_attributes!(:asset => requests_with_same_submission.first.target_asset) }
-
end
-
end
-
-
1
def validate_tags_not_repeated_for_submission!(requests, tags_to_wells)
-
submission_to_tag = requests.select{ |request| request.asset }.map{ |request| [request.submission_id, tags_to_wells[request.asset.map.description].map_id ] }
-
raise "Duplicate tags will be assigned to a pooled tube" if submission_to_tag != submission_to_tag.uniq
-
-
nil
-
end
-
-
1
def unlink_tag_instances_from_wells(requests)
-
requests.each do |request|
-
request.asset.untag!
-
end
-
end
-
-
1
def map_tags_to_wells(tag_group, plate)
-
tags_to_wells = {}
-
wells = plate.wells_sorted_by_map_id
-
sorted_tags = tag_group.tags_sorted_by_map_id
-
current_well = wells.first
-
-
1.upto(plate.size) do |index|
-
tags_to_wells[Map::Coordinate.vertical_plate_position_to_description(index, plate.size)] = sorted_tags[(index-1) % sorted_tags.size]
-
end
-
-
tags_to_wells
-
end
-
-
1
def find_plates_from_batch(batch_id)
-
requests = find_batch_requests(batch_id)
-
plates = requests.select{ |request| request.asset.is_a?(Well) }.map{ |request| request.asset }.map{ |asset| asset.plate }.select{ |plate| plate }
-
plates.first
-
end
-
-
1
def map_asset_ids_to_normalised_index_by_submission(requests)
-
submissions_to_index = {}
-
asset_ids_to_index = {}
-
requests.map{|request| request.submission_id }.uniq.each_with_index{ |submission_id, index| submissions_to_index[submission_id] = index }
-
requests.map{|request| asset_ids_to_index[request.asset_id] = submissions_to_index[request.submission_id] }
-
-
asset_ids_to_index
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class AssignTubesToMultiplexedWellsTask < Task
-
-
1
belongs_to :purpose
-
-
1
class AssignTubesToWellsData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def partial
-
"assign_tubes_to_wells"
-
end
-
-
1
def included_for_do_task
-
[{:requests=>:asset}, :pipeline ]
-
end
-
-
1
def included_for_render_task
-
[{:requests=>:asset}, :pipeline ]
-
end
-
-
1
def create_render_element(request)
-
request.asset && AssignTubesToWellsData.new(request)
-
end
-
-
1
def render_task(workflow, params)
-
super
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_assign_requests_to_multiplexed_wells_task(self, params)
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class AssignTubesToWellsTask < Task
-
1
class AssignTubesToWellsData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && AssignTubesToWellsData.new(request)
-
end
-
-
1
def partial
-
"assign_tubes_to_wells_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_assign_tubes_to_wells_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_assign_tubes_to_wells_task(self, params)
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class AttachInfiniumBarcodeTask < Task
-
-
1
class AttachInfiniumBarcodeData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && AssignTagsData.new(request)
-
end
-
-
1
def partial
-
"attach_infinium_barcode_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_attach_infinium_barcode_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_attach_infinium_barcode_task(self, params)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
# This module can be included into ActiveRecord::Base classes to get the ability to specify the attributes
-
# that are present. You can think of this as metadata being stored about the column in the table: it's
-
# default value, whether it's required, if it has a set of values that are acceptable, or if it's numeric.
-
# Use the class method 'attribute' to define your attribute:
-
#
-
# attribute(:foo, :required => true)
-
# attribute(:bar, :default => 'Something', :in => [ 'Something', 'Other thing' ])
-
# attribute(:numeric, :integer => true)
-
# attribute(:dependent, :required => true, :if => ->(r) { r.foo == 'Yep' })
-
#
-
# Attribute information can be retrieved from the class through 'attributes', and each one of the attributes
-
# you define can be converted to a FieldInfo instance using 'to_field_info'.
-
1
module Attributable
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
1
base.class_eval do
-
# NOTE: Do not use 'attributes' because that's an ActiveRecord internal name
-
1
class_attribute :attribute_details, :instance_writer => false
-
1
self.attribute_details = []
-
1
class_attribute :association_details, :instance_writer => false
-
1
self.association_details = []
-
end
-
end
-
-
-
1
class CustomValidator < ActiveModel::EachValidator
-
1
def validate_each(record, attribute, value)
-
valid = record.validator_for(attribute).valid_options.include?(value)
-
record.errors.add(attribute,"is not a valid option") unless valid
-
valid
-
end
-
end
-
-
1
def attribute_details_for(*args)
-
self.class.attribute_details_for(*args)
-
end
-
-
1
def instance_defaults
-
6
self.class.attribute_details.inject({}) do |hash, attribute|
-
550
hash.tap { hash[ attribute.name ] = attribute.default_from(self) if attribute.validator? }
-
end
-
end
-
-
1
def attribute_value_pairs
-
self.class.attribute_details.inject({}) do |hash, attribute|
-
hash.tap { hash[attribute] = attribute.from(self) }
-
end
-
end
-
-
1
def association_value_pairs
-
self.class.association_details.inject({}) do |hash, attribute|
-
hash.tap { hash[attribute] = attribute.from(self) }
-
end
-
end
-
-
1
def field_infos
-
self.class.attribute_details.map do |detail|
-
detail.to_field_info(nil,self)
-
end
-
end
-
-
1
def required?(field)
-
attribute = self.class.attribute_details.detect { |attribute| attribute.name == field }
-
attribute ||= self.class.association_details.detect { |association| :"#{association.name}_id" == field }
-
attribute.try(:required?)
-
end
-
-
1
module ClassMethods
-
-
1
def attribute(name, options = {}, override_previous = false)
-
159
attribute = Attribute.new(self, name, options)
-
159
attribute.configure(self)
-
-
159
if override_previous
-
self.attribute_details = self.attribute_details.reject { |a| a.name == name }
-
self.attribute_details += [attribute]
-
2057
elsif self.attribute_details.detect { |a| a.name == name }.nil?
-
141
self.attribute_details += [attribute]
-
end
-
end
-
-
1
def association(name, instance_method, options = {})
-
8
association = Association.new(self, name, instance_method, options)
-
8
association.configure(self)
-
8
self.association_details += [association]
-
end
-
-
1
def defaults
-
6
attribute_details.inject({}) do |hash, attribute|
-
550
hash.tap { hash[ attribute.name ] = attribute.default }
-
end
-
end
-
-
1
def attribute_names
-
attribute_details.map(&:name)
-
end
-
-
1
def attribute_details_for(attribute_name)
-
attribute_details.detect { |d| d.name.to_sym == attribute_name.to_sym } or raise StandardError, "Unknown attribute #{attribute_name}"
-
end
-
end
-
-
1
class Association
-
1
module Target
-
1
def self.extended(base)
-
6
base.class_eval do
-
6
include InstanceMethods
-
end
-
end
-
-
1
def for_select_association
-
all(:order => 'name ASC').map(&:for_select_dropdown).compact
-
end
-
-
1
def default
-
nil
-
end
-
-
1
module InstanceMethods
-
1
def for_select_dropdown
-
[ self.name, self.id ]
-
end
-
end
-
end
-
-
1
attr_reader :name
-
-
1
def initialize(owner, name, method, options = {})
-
8
@owner, @name, @method = owner, name, method
-
8
@required = !!options.delete(:required) || false
-
8
@scope = Array(options.delete(:scope))
-
end
-
-
1
def required?
-
@required
-
end
-
-
1
def optional?
-
not self.required?
-
end
-
-
1
def assignable_attribute_name
-
16
:"#{@name}_#{@method}"
-
end
-
-
1
def from(record)
-
record.send(@name).send(@method)
-
end
-
-
1
def display_name
-
Attribute::find_display_name(@owner, name)
-
end
-
-
1
def kind
-
FieldInfo::SELECTION
-
end
-
-
1
def find_default(*args)
-
nil
-
end
-
-
1
def selection?
-
true
-
end
-
-
1
def selection_options(_)
-
get_scoped_selection.all.map(&@method.to_sym).sort
-
end
-
-
1
def to_field_info(*args)
-
FieldInfo.new(
-
:display_name => display_name,
-
:key => assignable_attribute_name,
-
:kind => kind,
-
:selection => selection_options(nil)
-
)
-
end
-
-
1
def get_scoped_selection
-
@scope.inject(@owner.reflections[@name.to_sym].klass){|k,v| k.send(v.to_sym) }
-
end
-
1
private :get_scoped_selection
-
-
1
def configure(target)
-
8
target.class_eval(%Q{
-
8
def #{assignable_attribute_name}=(value)
-
8
record = self.class.reflections[:#{@name}].klass.find_by_#{@method}(value) or
-
8
raise ActiveRecord::RecordNotFound, "Could not find #{@name} with #{@method} \#{value.inspect}"
-
8
send(:#{@name}=, record)
-
end
-
-
8
def #{assignable_attribute_name}
-
8
send(:#{@name}).send(:#{@method})
-
end
-
})
-
end
-
end
-
-
1
class Attribute
-
1
attr_reader :name
-
1
attr_reader :default
-
-
1
alias_method :assignable_attribute_name, :name
-
-
1
def initialize(owner, name, options = {})
-
159
@owner, @name, @options = owner, name.to_sym, options
-
159
@default = options.delete(:default)
-
159
@required = !!options.delete(:required) || false
-
end
-
-
1
def from(record)
-
record[self.name]
-
end
-
-
1
def default_from(origin=nil)
-
return nil if origin.nil?
-
return origin.validator_for(name).default if validator?
-
end
-
-
1
def validator?
-
434
@options.key?(:validator)
-
end
-
-
1
def required?
-
@required
-
318
end
-
-
1
def optional?
-
159
not self.required?
-
end
-
-
1
def numeric?
-
159
@options.key?(:integer)
-
end
-
-
1
def float?
-
159
@options.key?(:positive_float)
-
end
-
-
1
def boolean?
-
220
@options.key?(:boolean)
-
end
-
-
1
def fixed_selection?
-
159
@options.key?(:in)
-
end
-
-
1
def selection?
-
fixed_selection?||@options.key?(:selection)
-
end
-
-
1
def method?
-
159
@options.key?(:with_method)
-
end
-
-
1
def validate_method
-
@options[:with_method]
-
end
-
-
1
def minimum
-
@options[:minimum]||0
-
end
-
-
1
def selection_values
-
19
@options[:in]
-
end
-
-
1
def valid_format
-
163
@options[:with]
-
end
-
-
1
def valid_format?
-
159
valid_format
-
end
-
-
1
def configure(model)
-
159
conditions = @options.slice(:if)
-
159
save_blank_value = @options.delete(:save_blank)
-
159
allow_blank = save_blank_value
-
-
159
model.with_options(conditions) do |object|
-
# false.blank? == true, so we exclude booleans here, they handle themselves further down.
-
159
object.validates_presence_of(name) if self.required? && !self.boolean?
-
159
object.with_options(:allow_nil => self.optional?, :allow_blank => allow_blank) do |required|
-
159
required.validates_inclusion_of(name, :in => [true, false]) if self.boolean?
-
159
required.validates_numericality_of(name, :only_integer => true) if self.numeric?
-
159
required.validates_numericality_of(name, :greater_than => 0) if self.float?
-
159
required.validates_inclusion_of(name, :in => self.selection_values, :allow_false => true) if self.fixed_selection?
-
159
required.validates_format_of(name, :with => self.valid_format) if self.valid_format?
-
159
required.validates name, :custom => true if self.validator?
-
159
required.validate(self.validate_method) if self.method?
-
end
-
end
-
-
159
unless save_blank_value
-
159
model.class_eval(%Q{
-
before_validation do |record|
-
159
value = record.#{name}
-
159
record.#{name}= nil if value and value.blank?
-
end
-
})
-
end
-
-
159
unless (condition = conditions[:if]).nil?
-
11
model.class_eval(%Q{
-
before_validation do |record|
-
11
record.#{name}= nil unless record.#{condition}
-
end
-
})
-
end
-
end
-
-
1
def self.find_display_name(klass, name)
-
translation = I18n.t("metadata.#{ klass.name.underscore.gsub('/', '.') }.#{ name }")
-
if translation.is_a?(Hash) # translation found, we return the label
-
return translation[:label]
-
else
-
superclass = klass.superclass
-
if superclass != ActiveRecord::Base # a subclass , try the superclass name scope
-
return find_display_name(superclass, name)
-
else # translation not found
-
return translation # shoulb be an error message, so that's ok
-
end
-
end
-
end
-
-
1
def display_name
-
Attribute::find_display_name(@owner, name)
-
end
-
-
1
def find_default(object = nil, metadata = nil)
-
default_from(metadata) || object.try(self.name) || self.default
-
end
-
-
1
def kind
-
return FieldInfo::SELECTION if self.selection?
-
return FieldInfo::BOOLEAN if self.boolean?
-
return FieldInfo::NUMERIC if self.numeric? || self.float?
-
FieldInfo::TEXT
-
end
-
-
1
def selection_from_metadata(metadata)
-
return nil unless metadata.present?
-
return metadata.validator_for(name).valid_options.to_a if validator?
-
end
-
-
1
def selection_options(metadata)
-
self.selection_values||selection_from_metadata(metadata)||[]
-
end
-
-
1
def to_field_info(object = nil, metadata = nil)
-
options = {
-
# TODO[xxx]: currently only working for metadata, the only place attributes are used
-
:display_name => display_name,
-
:key => assignable_attribute_name,
-
:default_value => find_default(object,metadata),
-
:kind => kind,
-
:required => required?
-
}
-
options.update(:selection => selection_options(metadata)) if self.selection?
-
options.update(:step => 1, :min => self.minimum) if self.numeric?
-
options.update(:step => 0.1, :min => 0) if self.float?
-
FieldInfo.new(options)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class BaitLibrary < ActiveRecord::Base
-
1
module Associations
-
1
def self.included(base)
-
2
base.class_eval do
-
2
belongs_to :bait_library
-
end
-
end
-
end
-
-
1
class Supplier < ActiveRecord::Base
-
1
self.table_name =('bait_library_suppliers')
-
-
# The names of suppliers needs to be unique
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name
-
-
1
scope :visible, -> { where(:visible => true) }
-
-
# They supply many bait libraries
-
1
has_many :bait_libraries, :foreign_key => :bait_library_supplier_id
-
-
1
def hide
-
self.visible = false
-
save!
-
end
-
end
-
-
# All bait libraries belong to a supplier
-
1
belongs_to :bait_library_supplier, :class_name => 'BaitLibrary::Supplier'
-
1
validates_presence_of :bait_library_supplier
-
-
# Within a supplier we have a unique identifier for each bait library. Custom bait libraries
-
# do not have this identifier, so nil is permitted.
-
1
validates_uniqueness_of :supplier_identifier, :scope => :bait_library_supplier_id, :allow_nil => true
-
1
before_validation :blank_as_nil
-
-
1
def blank_as_nil
-
self.supplier_identifier = nil if self.supplier_identifier.blank?
-
end
-
1
private :blank_as_nil
-
-
# The names of the bait library are considered unique within the supplier
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :scope => :bait_library_supplier_id
-
-
# All bait libraries target a specific species and cannot be mixed
-
1
validates_presence_of :target_species
-
-
# All bait libraries have a bait library type
-
1
belongs_to :bait_library_type
-
-
1
scope :visible, -> { where(:visible => true) }
-
-
1
def hide
-
self.visible = false
-
save!
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2015 Genome Research Ltd.
-
1
class BaitLibraryLayout < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include ModelExtensions::BaitLibraryLayout
-
-
# So we can track who is requesting the layout of the bait libraries
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
# Bait libraries are laid out on a specific plate only once.
-
1
belongs_to :plate
-
1
validates_presence_of :plate
-
1
validates_uniqueness_of :plate_id
-
-
# The layout of the bait libraries is recorded so that we can see what happened. It is serialized in a compact
-
# form that maps the bait library to the wells it was put into, but can be accessed in the reverse.
-
1
serialize :layout
-
1
validates_unassigned :layout
-
-
1
def well_layout
-
{}.tap do |well_to_name|
-
layout.map do |name, locations|
-
locations.map { |l| well_to_name[l] = name }
-
end
-
end
-
end
-
-
# Records the assignment of the bait library to a particular well
-
1
def record_bait_library_assignment(well, bait_library)
-
self.layout ||= Hash.new { |h,k| h[k] = [] }
-
self.layout[bait_library.name].push(well.map.description)
-
end
-
1
private :record_bait_library_assignment
-
-
# Before creation the layout of the bait libraries on the plate must be performed, based on the information
-
# specified as part of the submissions that lead to this plate.
-
1
before_create :layout_bait_libraries_on_plate
-
1
def layout_bait_libraries_on_plate
-
# To improve the performance we store the aliquot IDs that need each of the individual bait libraries
-
# attached to them in a hash. Then we'll be able to bulk update them later.
-
bait_libraries_to_aliquot_ids = Hash.new { |h,k| h[k] = [] }
-
each_bait_library_assignment do |well, bait_library|
-
bait_libraries_to_aliquot_ids[bait_library.id].concat(well.aliquot_ids)
-
record_bait_library_assignment(well, bait_library)
-
end
-
-
# Bulk update the aliquots with the appropriate bait libraries
-
bait_libraries_to_aliquot_ids.each do |bait_library_id, aliquot_ids|
-
Aliquot.update_all("bait_library_id=#{bait_library_id}", [ 'id IN (?)', aliquot_ids ])
-
end
-
end
-
1
private :layout_bait_libraries_on_plate
-
-
1
def each_bait_library_assignment(&block)
-
plate.stock_wells.each do |well, stock_wells|
-
bait_library = stock_wells.map { |w| w.requests_as_source.where_is_not_a?(TransferRequest).for_submission_id(well.pool_id).first }.compact.map(&:request_metadata).map(&:bait_library).uniq
-
raise StandardError, "Multiple bait libraries found for #{well.map.description} on plate #{well.plate.sanger_human_barcode}" if bait_library.size > 1
-
yield(well, bait_library.first)
-
end
-
end
-
1
private :each_bait_library_assignment
-
-
# Generates the layout of bait libraries for preview. In other words, none of the actually assignment is
-
# done, just the recording, which would fail validation if an attempt was then made to save it. So this is
-
# safe to do.
-
1
def generate_for_preview
-
each_bait_library_assignment do |well, bait_library|
-
record_bait_library_assignment(well, bait_library)
-
end
-
end
-
1
private :generate_for_preview
-
-
# This method can be used to view a previous of what will happen when the bait libraries are laid out
-
# on a plate.
-
1
def self.preview!(attributes = {}, &block)
-
new(attributes, &block).tap do |layout|
-
raise ActiveRecord::RecordInvalid, layout unless layout.valid?
-
layout.unsaved_uuid!
-
layout.send(:generate_for_preview)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class BaitLibraryType < ActiveRecord::Base
-
1
has_many :bait_libraries
-
-
# Types have names, need to be unique
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name
-
-
1
scope :visible, -> { where(:visible => true) }
-
-
1
def hide
-
self.visible = false
-
save!
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class Barcode
-
# Anything that has a barcode is considered barcodeable.
-
1
module Barcodeable
-
1
def self.included(base)
-
3
base.class_eval do
-
3
before_create :set_default_prefix
-
3
class_attribute :prefix
-
3
self.prefix = "NT"
-
-
3
if ActiveRecord::Base.observers.include?(:amqp_observer)
-
after_save :broadcast_barcode, :if => :barcode_changed?
-
end
-
-
end
-
end
-
-
1
def generate_barcode
-
self.barcode = AssetBarcode.new_barcode
-
end
-
-
1
def broadcast_barcode
-
AmqpObserver.instance << Messenger.new(:template=>'BarcodeIO',:root=>'barcode',:target=>self)
-
end
-
-
1
def barcode_type
-
'SangerEan13'
-
end
-
-
1
def set_default_prefix
-
self.barcode_prefix ||= BarcodePrefix.find_by_prefix(self.prefix)
-
end
-
1
private :set_default_prefix
-
-
1
def sanger_human_barcode
-
return nil if self.barcode.nil?
-
self.prefix + self.barcode.to_s + Barcode.calculate_checksum(self.prefix, self.barcode)
-
end
-
-
1
def ean13_barcode
-
return nil unless barcode.present? and prefix.present?
-
Barcode.calculate_barcode(self.prefix, self.barcode.to_i).to_s
-
end
-
1
alias_method :machine_barcode, :ean13_barcode
-
-
1
def role
-
return nil if no_role?
-
stock_plate.wells.first.requests.first.role
-
end
-
-
1
def no_role?
-
case
-
when stock_plate.nil?
-
return true
-
when stock_plate.wells.first.nil?
-
return true
-
when stock_plate.wells.first.requests.first.nil?
-
return true
-
else
-
false
-
end
-
end
-
-
1
def external_identifier
-
sanger_human_barcode
-
end
-
-
-
end
-
-
1
InvalidBarcode = Class.new(StandardError)
-
-
# Sanger barcoding scheme
-
-
1
def self.prefix_to_number(prefix)
-
first = prefix.getbyte(0)-64
-
second = prefix.getbyte(1)-64
-
first = 0 if first < 0
-
second = 0 if second < 0
-
return ((first * 27) + second) * 1000000000
-
end
-
-
# NT23432S => 398002343283
-
1
private
-
1
def self.calculate_sanger_barcode(prefix, number)
-
number_s = number.to_s
-
raise ArgumentError, "Number : #{number} to big to generate a barcode." if number_s.size > 7
-
human = prefix + number_s + calculate_checksum(prefix, number)
-
barcode = prefix_to_number(prefix) + (number * 100)
-
barcode = barcode + human.getbyte(human.length-1)
-
end
-
-
1
def self.calculate_barcode(prefix, number)
-
barcode = calculate_sanger_barcode(prefix, number)
-
barcode*10+calculate_EAN13(barcode)
-
end
-
-
1
def self.calculate_checksum(prefix, number)
-
string = prefix + number.to_s
-
len = string.length
-
sum = 0
-
string.each_byte do |byte|
-
sum += byte * len
-
len = len - 1
-
end
-
return (sum % 23 + 'A'.getbyte(0)).chr
-
end
-
-
1
def self.split_barcode(code)
-
code = code.to_s
-
if code.size > 11 && code.size < 14
-
# Pad with zeros
-
while code.size < 13
-
code = "0" + code
-
end
-
end
-
if /^(...)(.*)(..)(.)$/ =~ code
-
prefix, number, check, printer_check = $1, $2, $3, $4
-
end
-
[prefix, number.to_i, check.to_i]
-
end
-
-
1
def self.split_human_barcode(code)
-
if /^(..)(.*)(.)$/ =~code
-
[$1, $2, $3]
-
end
-
end
-
-
1
def self.number_to_human(code)
-
barcode = barcode_to_human(code)
-
prefix, number, check = split_human_barcode(barcode)
-
return number
-
end
-
-
1
def self.prefix_from_barcode(code)
-
barcode = barcode_to_human(code)
-
prefix, number, check = split_human_barcode(barcode)
-
return prefix
-
end
-
-
1
def self.prefix_to_human(prefix)
-
human_prefix = ((prefix.to_i/27)+64).chr + ((prefix.to_i%27)+64).chr
-
end
-
-
1
def self.human_to_machine_barcode(human_barcode)
-
human_prefix, bcode, human_suffix = split_human_barcode(human_barcode)
-
# Bugfix Exception 8:39 am Dec 22th 2015
-
# undefined method `+' for nil:NilClass app/models/barcode.rb:101:in `calculate_checksum'
-
# Incorrect barcode format
-
if human_prefix.nil? || Barcode.calculate_checksum(human_prefix, bcode) != human_suffix
-
raise InvalidBarcode, "The human readable barcode was invalid, perhaps it was mistyped?"
-
else
-
calculate_barcode(human_prefix,bcode.to_i)
-
end
-
end
-
-
1
def self.barcode_to_human(code)
-
bcode = nil
-
prefix, number, check = self.split_barcode(code)
-
human_prefix = self.prefix_to_human(prefix)
-
if calculate_barcode(human_prefix, number.to_i) == code.to_i
-
bcode = "#{human_prefix}#{number.to_s}#{check.chr}"
-
end
-
bcode
-
end
-
-
# Returns the Human barcode or raises an InvalidBarcode exception if there is a problem. The barcode is
-
# considered invalid if it does not translate to a Human barcode or, when the optional +prefix+ is specified,
-
# its human equivalent does not match.
-
1
def self.barcode_to_human!(code, prefix = nil)
-
human_barcode = barcode_to_human(code) or raise InvalidBarcode, "Barcode #{ code } appears to be invalid"
-
unless prefix.nil? or split_human_barcode(human_barcode).first == prefix
-
raise InvalidBarcode, "Barcode #{ code } (#{ human_barcode }) does not match prefix #{ prefix }"
-
end
-
human_barcode
-
end
-
-
1
def self.barcode_lookup(code)
-
prefix, number, check = self.split_barcode(code)
-
prefix = prefix_to_human(prefix)
-
human_code = self.barcode_to_human(code)
-
return nil unless human_code
-
-
case prefix
-
when "ID"
-
user = User.find_by_barcode human_code
-
return user.login if user
-
when "LE"
-
implement = Implement.find_by_barcode human_code
-
return implement.name if implement
-
end
-
-
return human_code
-
end
-
-
1
def self.check_EAN(code)
-
#the EAN checksum is calculated so that the EAN of the code with checksum added is 0
-
#except the new column (the checksum) start with a different weight (so the previous column keep the same weight)
-
calculate_EAN(code, 1) == 0
-
end
-
-
1
def self.calculate_EAN13(code)
-
calculate_EAN(code)
-
end
-
-
1
private
-
1
def self.calculate_EAN(code, initial_weight=3)
-
#The EAN is calculated by adding each digit modulo 10 ten weighted by 1 or 3 ( in seq)
-
code = code.to_i
-
ean = 0
-
weight = initial_weight
-
while code >0
-
code, c = code.divmod 10
-
ean += c*weight % 10
-
weight = weight == 1 ? 3 : 1
-
end
-
-
(10 -ean) % 10
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class BarcodePrefix < ActiveRecord::Base
-
1
has_many :assets
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class BarcodePrinter < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
-
1
belongs_to :barcode_printer_type
-
1
validates_presence_of :barcode_printer_type
-
1
scope :include_barcode_printer_type, -> { includes(:barcode_printer_type) }
-
1
scope :alphabetical, -> { order('name ASC') }
-
-
1
def service_url
-
configatron.barcode_service_url
-
end
-
-
1
def service
-
@service ||= self.class.service
-
end
-
-
1
def printer_type_id
-
self.barcode_printer_type.printer_type_id
-
end
-
-
1
def print_labels(labels, barcode_prefix=nil, barcode_type= "short",study_name=nil, user_login=nil)
-
service.print_labels(labels, name, printer_type_id,
-
:prefix => barcode_prefix,
-
:type => barcode_type,
-
:study_name => study_name,
-
:user_login => user_login)
-
end
-
1
def self.print(labels, printer_name, *args)
-
printer = BarcodePrinter.find_by_name(printer_name) or raise ActiveRecord::RecordNotFound, "Could not find barcode printer '#{printer_name.inspect}'"
-
-
printer.print_labels(labels, *args)
-
-
end
-
-
1
def self.verify(number)
-
service.verify(number)
-
end
-
-
1
@@service = nil
-
1
class << self
-
1
def service
-
return @@service unless @@service.nil?
-
return PrintBarcode::Service.new(configatron.barcode_service_url)
-
end
-
1
alias_method(:init_service, :service)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class BarcodePrinterType < ActiveRecord::Base
-
1
has_many :barcode_printers
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :on => :create, :message => "already in use"
-
# printer_type_id is used by the perl script printing service to decide on the positioning of information on the label
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class BarcodePrinterType1DTube < BarcodePrinterType
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class BarcodePrinterType384Plate < BarcodePrinterType
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class BarcodePrinterType96Plate < BarcodePrinterType
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
require 'timeout'
-
require "tecan_file_generation"
-
require 'aasm'
-
-
class Batch < ActiveRecord::Base
-
include Api::BatchIO::Extensions
-
include Api::Messages::FlowcellIO::Extensions
-
-
self.per_page = 500
-
include AASM
-
include SequencingQcBatch
-
include Commentable
-
include Uuid::Uuidable
-
include ModelExtensions::Batch
-
include StandardNamedScopes
-
-
validate :requests_have_same_read_length, :cluster_formation_requests_must_be_over_minimum, :all_requests_are_ready?, :on => :create
-
-
def all_requests_are_ready?
-
# Checks that SequencingRequests have at least one LibraryCreationRequest in passed status before being processed (as refered by #75102998)
-
unless requests.all?(&:ready?)
-
errors.add :base, "All requests must be ready to be added to a batch"
-
end
-
end
-
-
def cluster_formation_requests_must_be_over_minimum
-
if (!pipeline.min_size.nil?) && (requests.size < pipeline.min_size)
-
errors.add :base, "You must create batches of at least " + pipeline.min_size.to_s+" requests in the pipeline " + pipeline.name
-
end
-
end
-
-
def requests_have_same_read_length
-
unless pipeline.is_read_length_consistent_for_batch?(self)
-
errors.add :base, "The selected requests must have the same values in their 'Read length' field."
-
end
-
end
-
-
extend EventfulRecord
-
has_many_events
-
has_many_lab_events
-
-
DEFAULT_VOLUME = 13
-
-
include ::Batch::PipelineBehaviour
-
include ::Batch::StateMachineBehaviour
-
include ::Batch::TecanBehaviour
-
-
belongs_to :user, :foreign_key => "user_id"
-
belongs_to :assignee, :class_name => "User", :foreign_key => "assignee_id"
-
-
has_many :failures, :as => :failable
-
has_many :messengers, :as => :target, :inverse_of => :target
-
-
# Named scope for search by query string behavior
-
scope :for_search_query, ->(query,with_includes) {
-
conditions = [ 'id=?', query ]
-
if user = User.find_by_login(query)
-
conditions = [ 'user_id=?', user.id ]
-
end
-
where(conditions)
-
}
-
-
scope :includes_for_ui, -> { limit(5).includes(:user) }
-
scope :pending_for_ui, -> { where(:state => 'pending', :production_state => nil ).latest_first }
-
scope :released_for_ui, -> { where(:state => 'released', :production_state => nil ).latest_first }
-
scope :completed_for_ui, -> { where(:state => 'completed', :production_state => nil ).latest_first }
-
scope :failed_for_ui, -> { where( :production_state => 'fail').latest_first }
-
scope :in_progress_for_ui, -> { where(:state => 'started', :production_state => nil ).latest_first }
-
-
scope :latest_first, -> { order('created_at DESC') }
-
1
-
delegate :size, :to => :requests
-
-
# Fail was removed from State Machine (as a state) to allow the addition of qc_state column and features
-
1
def fail(reason, comment, ignore_requests=false)
-
# We've deprecated the ability to fail a batch but not its requests.
-
# Keep this check here until we're sure we haven't missed anything.
-
raise StandardError, "Can not fail batch without failing requests" if ignore_requests
-
# create failures
-
self.failures.create(:reason => reason, :comment => comment, :notify_remote => false)
-
-
self.requests.each do |request|
-
request.failures.create(:reason => reason, :comment => comment, :notify_remote => true)
-
unless request.asset && request.asset.resource?
-
EventSender.send_fail_event(request.id, reason, comment, self.id)
-
end
-
end
-
-
self.production_state = "fail"
-
self.save!
-
end
-
-
# Fail specific items on this batch
-
1
def fail_batch_items(requests, reason, comment, fail_but_charge=false)
-
checkpoint = true
-
-
requests.each do |key, value|
-
if value == "on"
-
logger.debug "SENDING FAIL FOR REQUEST #{key}, BATCH #{self.id}, WITH REASON #{reason}"
-
unless key == "control"
-
ActiveRecord::Base.transaction do
-
request = self.requests.find(key)
-
request.customer_accepts_responsibility! if fail_but_charge
-
request.failures.create(:reason => reason, :comment => comment, :notify_remote => true)
-
EventSender.send_fail_event(request.id, reason, comment, self.id)
-
end
-
end
-
else
-
checkpoint = false
-
end
-
end
-
-
update_batch_state(reason, comment) if checkpoint
-
end
-
-
1
def update_batch_state(reason, comment)
-
if self.requests.all?(&:terminated?)
-
self.failures.create(:reason => reason, :comment => comment, :notify_remote => false)
-
self.production_state = "fail"
-
self.save!
-
end
-
end
-
-
1
def failed?
-
self.production_state == "fail"
-
end
-
-
# Used in auto_batch view to disable the submit button if the batch was already passed to Auto QC
-
1
def in_process?
-
statuses = qc_states
-
statuses.delete_at(0)
-
statuses.include?(self.qc_state)
-
end
-
-
# Tests whether this Batch has any associated LabEvents
-
1
def has_event(event_name)
-
lab_events.any? { |event| event_name.downcase == event.description.try(:downcase) }
-
end
-
-
1
def underrun
-
self.has_limit? ? (self.item_limit - self.batch_requests.size) : 0
-
end
-
-
1
def control
-
self.requests.detect { |request| request.try(:asset).try(:resource?) }
-
end
-
-
1
def has_control?
-
!self.control.nil?
-
end
-
-
# Sets the position of the requests in the batch to their index in the array.
-
1
def assign_positions_to_requests!(request_ids_in_position_order)
-
disparate_ids = batch_requests.map(&:request_id) - request_ids_in_position_order
-
raise StandardError, "Can only sort all requests at once" unless disparate_ids.empty?
-
-
BatchRequest.transaction do
-
batch_requests.each do |batch_request|
-
batch_request.move_to_position!(request_ids_in_position_order.index(batch_request.request_id)+1)
-
end
-
end
-
end
-
-
1
alias_method :ordered_requests, :requests
-
-
1
def shift_item_positions(position, number)
-
return unless number
-
BatchRequest.transaction do
-
batch_requests.each do |batch_request|
-
next unless batch_request.position >= position
-
next if batch_request.request.asset.try(:resource?)
-
batch_request.move_to_position!(batch_request.position + number)
-
end
-
end
-
-
ordered_requests
-
end
-
-
1
def assigned_user
-
self.assignee.try(:login) || ''
-
end
-
-
1
def start_requests
-
self.requests.with_assets_for_starting_requests.not_failed.map(&:start!)
-
end
-
-
1
def input_group
-
pipeline.group_requests requests
-
end
-
-
1
def input_plate_group
-
requests.map(&:asset).select(&:present?).group_by(&:plate)
-
end
-
-
1
def input_group_sorted_by_map_id
-
requests.map(&:asset).select(&:present?).sort_by(&:map_id).group_by(&:parent)
-
end
-
-
1
def output_group
-
pipeline.group_requests requests.with_target, :by_target => true
-
end
-
-
1
def output_group_by_holder
-
pipeline.group_requests requests.with_target, :by_target => true, :group_by_holder_only => true
-
end
-
-
1
def output_plate_group
-
requests.select { |r| r.target_asset != r.asset}.map(&:target_asset).select(&:present?).group_by(&:plate)
-
end
-
-
1
def output_plates
-
holder_ids = Request.get_target_holder_asset_id_map(request_ids).values
-
Plate.find(holder_ids, :group => :barcode)
-
end
-
-
## WARNING! This method is used in the sanger barcode gem. Do not remove it without
-
## refactoring the sanger barcode gem.
-
1
def output_plate_purpose
-
output_plates[0].plate_purpose unless output_plates[0].nil?
-
end
-
-
1
def output_plate_role
-
requests.first.try(:role)
-
end
-
-
1
def output_plate_in_batch?(barcode)
-
return false if barcode.nil?
-
return false if Plate.find_by_barcode(barcode).nil?
-
output_plates.any? { |plate| plate.barcode == barcode }
-
end
-
-
-
1
def plate_group_barcodes
-
return nil unless pipeline.group_by_parent || requests.first.target_asset.is_a?(Well)
-
latest_plate_group = output_plate_group
-
return latest_plate_group unless latest_plate_group.empty?
-
return input_plate_group
-
end
-
-
1
def plate_barcode(barcode)
-
if barcode
-
return barcode
-
else
-
return requests.first.target_asset.plate.barcode
-
end
-
end
-
-
1
def mpx_library_name
-
mpx_name = ""
-
if self.multiplexed? && self.requests.size > 0
-
mpx_library_tube = self.requests[0].target_asset.child
-
if ! mpx_library_tube.nil?
-
mpx_name = mpx_library_tube.name
-
end
-
end
-
mpx_name
-
end
-
-
1
def display_tags?
-
self.multiplexed?
-
end
-
-
-
# Returns meaningful events excluding discriptors/descriptor_fields clutter
-
1
def formatted_events
-
ev = self.lab_events
-
d = []
-
unless ev.empty?
-
ev.sort_by{ |i| i[:created_at] }.each do |t|
-
if t.descriptors
-
if g = t.descriptor_value("task")
-
d << {"task" => g, "description" => t.description, "message" => t.message, "data" => t.data, "created_at" => t.created_at}
-
end
-
end
-
end
-
end
-
d
-
end
-
-
-
1
def multiplexed_items_with_unique_library_ids
-
requests.map { |r| r.target_asset.children }.flatten.uniq
-
end
-
-
1
def assets
-
requests.map(&:target_asset)
-
end
-
-
1
def source_assets
-
requests.map(&:asset)
-
end
-
-
# Source Labware returns the physical pieces of lawbare (ie. a plate for wells, but stubes for tubes)
-
1
def source_labware
-
requests.map(&:asset).map(&:labware).uniq
-
end
-
-
1
def verify_tube_layout(barcodes, user = nil)
-
self.requests.each do |request|
-
barcode = barcodes["#{request.position}"]
-
unless barcode.blank? || barcode == "0"
-
unless barcode.to_i == request.asset.barcode.to_i
-
self.errors.add(:base,"The tube at position #{request.position} is incorrect.")
-
end
-
end
-
end
-
if self.errors.empty?
-
self.lab_events.create(:description => "Tube layout verified", :user => user)
-
return true
-
else
-
return false
-
end
-
end
-
-
1
def release_pending_requests
-
# We set the unusued requests to pendind.
-
# this is to allow unused well to be cherry-picked again
-
requests.each do |request|
-
detach_request(request) if request.started?
-
end
-
end
-
-
# Remove the request from the batch and remove asset information
-
1
def remove_request_ids(request_ids, reason=nil, comment=nil)
-
ActiveRecord::Base.transaction do
-
request_ids.each do |request_id|
-
request = Request.find(request_id)
-
next if request.nil?
-
request.failures.create(:reason => reason, :comment => comment, :notify_remote => true)
-
self.detach_request(request)
-
end
-
update_batch_state(reason, comment)
-
end
-
end
-
1
alias_method(:recycle_request_ids, :remove_request_ids)
-
-
# Remove a request from the batch and reset it to a point where it can be put back into
-
# the pending queue.
-
1
def detach_request(request, current_user=nil)
-
ActiveRecord::Base.transaction do
-
request.add_comment("Used to belong to Batch #{self.id} removed at #{Time.now()}", current_user) unless current_user.nil?
-
self.pipeline.detach_request_from_batch(self, request)
-
end
-
end
-
-
1
def return_request_to_inbox(request, current_user=nil)
-
ActiveRecord::Base.transaction do
-
request.add_comment("Used to belong to Batch #{self.id} returned to inbox unstarted at #{Time.now()}", current_user) unless current_user.nil?
-
request.return_pending_to_inbox!
-
end
-
end
-
-
1
def remove_link(request)
-
request.batch = nil
-
end
-
-
1
def reset!(current_user)
-
ActiveRecord::Base.transaction do
-
self.discard!
-
-
self.requests.each do |request|
-
self.remove_link(request) # Remove link in all types of pipelines
-
self.return_request_to_inbox(request, current_user)
-
end
-
-
if self.requests.last.submission_id.present?
-
requests = Request.find_all_by_submission_id(self.requests.last.submission_id,
-
:conditions => ['state = ? AND request_type_id NOT IN (?)', 'pending', self.pipeline.request_type_ids])
-
requests.each do |request|
-
request.asset_id = nil
-
request.save!
-
end
-
end
-
end
-
end
-
-
1
def parent_of_purpose(name)
-
return nil if requests.empty?
-
requests.first.asset.ancestors.find(:first,:joins=>'INNER JOIN plate_purposes ON assets.plate_purpose_id = plate_purposes.id',:conditions=>{:plate_purposes=>{:name=>name}})
-
end
-
-
1
def swap(current_user, batch_info = {})
-
return false if batch_info.empty?
-
-
# Find the two lanes that are to be swapped
-
batch_request_left = BatchRequest.find_by_batch_id_and_position(batch_info['batch_1']['id'], batch_info['batch_1']['lane']) or self.errors.add("Swap: ", "The first lane cannot be found")
-
batch_request_right = BatchRequest.find_by_batch_id_and_position(batch_info['batch_2']['id'], batch_info['batch_2']['lane']) or self.errors.add("Swap: ", "The second lane cannot be found")
-
return unless batch_request_left.present? and batch_request_right.present?
-
-
ActiveRecord::Base.transaction do
-
# Update the lab events for the request so that they reference the batch that the request is moving to
-
batch_request_left.request.lab_events.each { |event| event.update_attributes!(:batch_id => batch_request_right.batch_id) if event.batch_id == batch_request_left.batch_id }
-
batch_request_right.request.lab_events.each { |event| event.update_attributes!(:batch_id => batch_request_left.batch_id) if event.batch_id == batch_request_right.batch_id }
-
-
# Swap the two batch requests so that they are correct. This involves swapping both the batch and the lane but ensuring that the
-
# two requests don't clash on position by removing one of them.
-
original_left_batch_id, original_left_position, original_right_request_id = batch_request_left.batch_id, batch_request_left.position, batch_request_right.request_id
-
batch_request_right.destroy
-
batch_request_left.update_attributes!(:batch_id => batch_request_right.batch_id, :position => batch_request_right.position)
-
batch_request_right = BatchRequest.create!(:batch_id => original_left_batch_id, :position => original_left_position, :request_id => original_right_request_id)
-
-
# Finally record the fact that the batch was swapped
-
batch_request_left.batch.lab_events.create!(:description => "Lane swap", :message => "Lane #{batch_request_right.position} moved to #{batch_request_left.batch_id} lane #{batch_request_left.position}", :user_id => current_user.id)
-
batch_request_right.batch.lab_events.create!(:description => "Lane swap", :message => "Lane #{batch_request_left.position} moved to #{batch_request_right.batch_id} lane #{batch_request_right.position}", :user_id => current_user.id)
-
end
-
-
return true
-
end
-
-
1
def study
-
self.studies.first
-
end
-
-
#TODO has_many :aliquots, :finder_sql => ...
-
1
def aliquots
-
self.requests.map(&:asset).compact.map(&:aliquots).flatten.compact
-
end
-
-
#TODO has_many :orders, :finder_sql => ...
-
1
def orders
-
self.requests.map(&:submission).compact.map(&:orders).flatten.compact
-
end
-
-
#not efficient, but not used often
-
1
def studies
-
#we use order and not aliquots because aliquots can be empty
-
self.orders.map(&:study).compact.uniq
-
end
-
-
1
def projects
-
self.orders.map(&:project).compact
-
end
-
-
1
def samples
-
requests.including_samples_from_target.map {|r| r.target_asset.samples }.flatten.uniq
-
end
-
-
1
def requests_by_study(*args)
-
self.requests.for_studies(*args).all
-
end
-
1
deprecate :requests_by_study
-
-
1
def plate_ids_in_study(study)
-
Plate.plate_ids_from_requests(self.requests.for_studies(study))
-
end
-
-
1
def space_left
-
[self.item_limit - self.batch_requests.count, 0].max
-
end
-
-
1
def add_control(control_name, control_count)
-
asset = Asset.find_by_name_and_resource(control_name, true)
-
-
control_count = self.space_left if control_count == 0
-
-
first_control = [3, (self.item_limit - control_count)].min
-
-
ActiveRecord::Base.transaction do
-
self.shift_item_positions(first_control+1, control_count)
-
(1..control_count).each do |index|
-
self.batch_requests.create!(
-
:request => self.pipeline.control_request_type.create_control!(:asset => asset, :study_id => 198),
-
:position => first_control+index
-
)
-
end
-
end
-
control_count
-
end
-
-
1
def total_volume_to_cherrypick
-
request = requests.first
-
return DEFAULT_VOLUME unless request.asset.is_a?(Well)
-
return DEFAULT_VOLUME unless request.target_asset.is_a?(Well)
-
request.target_asset.get_requested_volume
-
end
-
-
1
def self.prefix
-
"BA"
-
end
-
-
1
def self.valid_barcode?(code)
-
begin
-
Barcode.barcode_to_human!(code, self.prefix)
-
rescue
-
return false
-
end
-
-
if self.find_from_barcode(code).nil?
-
return false
-
end
-
-
true
-
end
-
-
1
def self.find_from_barcode(code)
-
human_batch_barcode = Barcode.number_to_human(code)
-
batch = Batch.find_by_barcode(human_batch_barcode)
-
batch ||= Batch.find_by_id(human_batch_barcode)
-
-
batch
-
end
-
-
1
def request_count
-
BatchRequest.count(:conditions => ["batch_id = ?", self.id ])
-
end
-
-
1
def pulldown_batch_report
-
report_data = CSV.generate( :row_sep => "\r\n") do |csv|
-
csv << pulldown_report_headers
-
-
self.requests.each do |request|
-
raise 'Invalid request data' unless request.valid_request_for_pulldown_report?
-
well = request.asset
-
#TODO[mb14] DRY it
-
tagged_well = well
-
while transfer_requests=tagged_well.requests.select { |r| r.is_a?(TransferRequest) } and transfer_requests.size == 1
-
target_well = transfer_requests.first.target_asset
-
break unless target_well.is_a?(Well)
-
tagged_well=target_well
-
tag_on_well = tagged_well.primary_aliquot.try(:tag)
-
if tag_on_well.present?
-
tag_name = tag_on_well.name
-
tag_expected_sequence = tag_on_well.oligo
-
tag_group_name = tag_on_well.tag_group.name if tag_on_well.tag_group.present?
-
break
-
end
-
end
-
-
sample = well.primary_aliquot.try(:sample)
-
csv << [
-
well.plate.sanger_human_barcode,
-
well.map.description,
-
well.study.try(:name),
-
request.target_asset.try(:barcode),
-
tag_group_name,
-
tag_name,
-
tag_expected_sequence,
-
sample.sanger_sample_id || sample.name,
-
well.parent.well_attribute.measured_volume,
-
well.parent.well_attribute.concentration
-
]
-
end
-
end
-
-
report_data
-
end
-
-
1
def pulldown_report_headers
-
['Plate', 'Well', 'Study','Pooled Tube', 'Tag Group', 'Tag', 'Expected Sequence', 'Sample Name', 'Measured Volume', 'Measured Concentration']
-
end
-
-
1
def show_actions?
-
self.released? == false or
-
self.pipeline.class.const_get(:ALWAYS_SHOW_RELEASE_ACTIONS)
-
end
-
-
1
def npg_set_state
-
complete = true
-
self.requests.each do |request|
-
unless request.asset.is_a_resource
-
event = request.events.family_pass_and_fail.first
-
if (event.nil?)
-
complete = false
-
end
-
end
-
end
-
-
if complete
-
self.state = "released"
-
self.qc_complete
-
self.save!
-
end
-
end
-
-
1
def show_fail_link?
-
self.released? && self.pipeline.sequencing?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
module Batch::PipelineBehaviour
-
def self.included(base)
-
base.class_eval do
-
# The associations with the pipeline
-
belongs_to :pipeline
-
attr_protected :pipeline_id
-
delegate :workflow, :item_limit, :multiplexed?, :to => :pipeline
-
delegate :tasks, :to => :workflow
-
-
# The validations that the pipeline & batch are correct
-
validates_presence_of :pipeline
-
-
# Validation of some of the batch information is left to the pipeline that it belongs to
-
validate do |record|
-
record.pipeline.validation_of_batch(record) if record.pipeline.present?
-
end
-
-
1
# The batch requires positions on it's requests if the pipeline does
-
delegate :requires_position?, :to => :pipeline
-
-
# Ensure that the batch is valid to be marked as completed
-
1
validate(:if => :completed?) do |record|
-
record.pipeline.validation_of_batch_for_completion(record)
-
end
-
end
-
end
-
-
def externally_released?
-
workflow.source_is_internal? && self.released?
-
end
-
-
def internally_released?
-
workflow.source_is_external? && self.released?
-
end
-
-
def show_actions?
-
return true if pipeline.is_a?(PulldownMultiplexLibraryPreparationPipeline) || pipeline.is_a?(CherrypickForPulldownPipeline)
-
!released?
-
end
-
-
def has_item_limit?
-
self.item_limit.present?
-
end
-
alias_method(:has_limit?, :has_item_limit?)
-
-
def events_for_completed_tasks
-
self.lab_events.select{ |le| le.description == "Complete" }
-
end
-
-
def tasks_for_completed_task_events(events)
-
completed_tasks = []
-
events.each do |event|
-
task_id = event.descriptors.detect{ |d| d.name == "task_id" }
-
if task_id
-
begin
-
task = Task.find(task_id.value)
-
rescue ActiveRecord::RecordNotFound
-
return []
-
end
-
unless task.nil?
-
completed_tasks << task
-
end
-
end
-
end
-
completed_tasks
-
end
-
-
def last_completed_task
-
unless self.events_for_completed_tasks.empty?
-
completed_tasks = self.tasks_for_completed_task_events(self.events_for_completed_tasks)
-
tasks = self.pipeline.workflow.tasks
-
tasks.sort!{ |a, b| b.sorted <=> a.sorted }
-
tasks.each do |task|
-
if completed_tasks.include?(task)
-
return task
-
end
-
end
-
return nil
-
end
-
end
-
-
def task_for_event(event)
-
tasks.detect { |task| task.name == event.description }
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014,2015 Genome Research Ltd.
-
module Batch::RequestBehaviour
-
def self.included(base)
-
base.class_eval do
-
has_one :batch_request, :inverse_of => :request, :dependent => :destroy
-
has_one :batch, :through => :batch_request
-
-
scope :include_for_batch_view, -> { includes(:batch_request,:asset,:target_asset,:request_metadata,:comments)}
-
-
# For backwards compatibility
-
def batch_requests; [batch_request].compact ; end
-
def batches; [batch].compact ; end
-
-
-
# Identifies all requests that are not part of a batch.
-
scope :unbatched,
-
joins('LEFT OUTER JOIN batch_requests ubr ON `requests`.`id`=`ubr`.`request_id`').
-
1
readonly(false).
-
where('`ubr`.`request_id` IS NULL')
-
delegate :position, :to=>:batch_request, :allow_nil=>true
-
end
-
end
-
-
def with_batch_id
-
yield batch.id if batch.present?
-
end
-
-
def recycle_from_batch!
-
ActiveRecord::Base.transaction do
-
self.return_for_inbox!
-
self.batch_request.destroy if self.batch_request.present?
-
self.save!
-
end
-
#self.detach
-
#self.batches -= [ batch ]
-
end
-
-
def create_batch_request!(*args)
-
# I think this is actually deprecated
-
create_batch_request(args)
-
end
-
-
def return_for_inbox!
-
# Valid for started, cancelled and pending batches
-
# Will raise an exception outside of this
-
self.cancel! if self.started?
-
self.detach! unless self.pending?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2014 Genome Research Ltd.
-
1
module Batch::StateMachineBehaviour
-
1
def self.included(base)
-
1
base.class_eval do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
1
aasm_state :pending
-
1
aasm_state :started, :enter => :start_requests
-
1
aasm_state :completed
-
1
aasm_state :released
-
1
aasm_state :discarded
-
-
# State Machine events
-
1
aasm_event :start do
-
1
transitions :to => :started, :from => [:pending, :started]
-
end
-
-
1
aasm_event :complete do
-
1
transitions :to => :completed, :from => [:started, :pending, :completed]
-
end
-
-
1
aasm_event :release do
-
1
transitions :to => :released, :from => [:completed, :started, :pending, :released]
-
end
-
-
1
aasm_event :discard do
-
1
transitions :to => :discarded, :from => [:pending]
-
end
-
-
# Some named scopes needed for finding the batches in a particular state
-
1
scope :pending, -> { where(:state => "pending") }
-
1
scope :started, -> { where(:state => "started") }
-
1
scope :completed, -> { where(:state => "completed") }
-
1
scope :released, -> { where(:state => "released") }
-
1
scope :failed, -> { where(:production_state => "fail") }
-
-
# We override the behaviour of a couple of events because they require user details.
-
1
alias_method_chain(:start!, :user)
-
1
alias_method_chain(:complete!, :user)
-
1
alias_method_chain(:release!, :user)
-
end
-
end
-
-
1
def finished?
-
completed? or released?
-
end
-
-
1
def editable?
-
pending? or started?
-
end
-
-
1
def start_with_user!(user)
-
pipeline.on_start_batch(self,user)
-
start_without_user!
-
end
-
-
1
def complete_with_user!(user)
-
complete_without_user! unless released?
-
create_complete_batch_event_for(user)
-
pipeline.post_finish_batch(self, user)
-
end
-
-
1
def create_complete_batch_event_for(user)
-
lab_events.create!(:batch => self, :user => user, :description => 'Complete').tap do |event|
-
event.add_descriptor Descriptor.new(:name => 'pipeline_id', :value => pipeline.id)
-
event.add_descriptor Descriptor.new(:name => 'pipeline', :value => pipeline.name)
-
event.save!
-
end
-
end
-
1
private :create_complete_batch_event_for
-
-
1
def release_with_user!(user)
-
requests.each { |request| pipeline.completed_request_as_part_of_release_batch(request) }
-
release_without_user!
-
create_release_batch_event_for(user)
-
pipeline.post_release_batch(self, user)
-
end
-
-
1
def create_release_batch_event_for(user)
-
lab_events.create!(:batch => self, :user => user, :description => 'Released').tap do |event|
-
event.add_descriptor Descriptor.new(:name => 'workflow_id', :value => workflow.id)
-
event.add_descriptor Descriptor.new(:name => 'workflow', :value => "Released from #{workflow.name}")
-
event.add_descriptor Descriptor.new(:name => 'task', :value => workflow.name)
-
event.add_descriptor Descriptor.new(:name => 'Released', :value => workflow.name)
-
event.save!
-
end
-
end
-
1
private :create_release_batch_event_for
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015 Genome Research Ltd.
-
1
module Batch::TecanBehaviour
-
1
def validate_for_tecan(target_barcode)
-
return false if user_id.nil?
-
return false if requests.nil? || requests.size == 0
-
-
requests.find_all_by_state("passed").each do |request|
-
next unless request.target_asset.plate.barcode == target_barcode
-
return false unless Well.find(request.asset).valid_well_on_plate
-
return false unless Well.find(request.target_asset).valid_well_on_plate
-
end
-
true
-
end
-
-
1
def generate_tecan_data(target_barcode, override_plate_type = nil)
-
# very slow
-
#return nil unless validate_for_tecan(target_barcode)
-
-
data_object = {
-
"user" => user.login ,
-
"time" => Time.now,
-
"source" => {},
-
"destination" => {}
-
}
-
requests.find(:all, :include=>[{:asset=>:plate},{:target_asset=>:plate}],:conditions=>{:state=>"passed"}).each do |request|
-
-
destination_barcode = request.target_asset.plate.barcode
-
next unless destination_barcode == target_barcode
-
-
full_source_barcode = request.asset.plate.barcode_for_tecan
-
full_destination_barcode = request.target_asset.plate.barcode_for_tecan
-
-
source_plate_name = request.asset.plate.stock_plate_name.gsub(/_/, "\s")
-
if override_plate_type
-
source_plate_name = override_plate_type
-
end
-
-
if data_object["source"][full_source_barcode].nil?
-
data_object["source"][full_source_barcode] = {"name" => source_plate_name, "plate_size" => request.asset.plate.size}
-
end
-
if data_object["destination"][full_destination_barcode].nil?
-
data_object["destination"][full_destination_barcode] = {
-
"name" => PlatePurpose.cherrypickable_default_type.first.name.gsub(/_/, "\s"),
-
"plate_size" => request.target_asset.plate.size
-
}
-
end
-
if data_object["destination"][full_destination_barcode]["mapping"].nil?
-
data_object["destination"][full_destination_barcode]["mapping"] = []
-
end
-
-
data_object["destination"][full_destination_barcode]["mapping"] << {
-
"src_well" => [full_source_barcode, request.asset.map.description],
-
"dst_well" => request.target_asset.map.description,
-
"volume" => (request.target_asset.get_picked_volume) }
-
end
-
-
data_object
-
end
-
-
1
def tecan_layout_plate_barcodes(target_barcode)
-
data_object = generate_tecan_data(target_barcode)
-
dest_barcode_index = Sanger::Robots::Tecan::Generator.barcode_to_plate_index(data_object["destination"])
-
source_barcode_index = Sanger::Robots::Tecan::Generator.source_barcode_to_plate_index(data_object["destination"])
-
[dest_barcode_index,source_barcode_index]
-
end
-
-
1
def tecan_gwl_file_as_text(target_barcode, volume_required = 13, plate_type = nil)
-
data_object = generate_tecan_data(target_barcode, plate_type)
-
Sanger::Robots::Tecan::Generator.mapping(data_object, volume_required.to_i)
-
end
-
-
1
def tecan_gwl_file(target_barcode,volume_required=13)
-
data_object = generate_tecan_data(target_barcode)
-
gwl_data = Sanger::Robots::Tecan::Generator.mapping(data_object, volume_required.to_i)
-
begin
-
year= Time.now.year
-
base_directory ="#{configatron.tecan_files_location}/#{year}"
-
unless File.exists?(base_directory)
-
FileUtils.mkdir base_directory
-
end
-
destinationbarcode = data_object["destination"].keys.join("_")
-
gwl_file = File.new("#{base_directory}/#{destinationbarcode}_batch_#{self.id}.gwl", "w")
-
gwl_file.write(gwl_data)
-
gwl_file.close
-
rescue
-
return false
-
end
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
class BatchRequest < ActiveRecord::Base
-
include Api::BatchRequestIO::Extensions
-
-
self.per_page = 500
-
include Uuid::Uuidable
-
-
belongs_to :batch
-
belongs_to :request, :inverse_of => :batch_request
-
-
scope :ordered, -> { order('position ASC') }
-
scope :at_position, ->(position) { { :conditions => { :position => position } } }
-
-
# Ensure that any requests that are added have a position that is unique and incremental in the batch,
-
# unless we're moving them around in the batch, in which case we assume it'll be valid.
-
attr_accessor :sorting_requests_within_batch
-
alias_method(:sorting_requests_within_batch?, :sorting_requests_within_batch)
-
1
-
delegate :requires_position?, :to => :batch
-
-
1
def need_to_check_position?
-
requires_position? and not sorting_requests_within_batch?
-
end
-
1
private :need_to_check_position?
-
-
1
validates_numericality_of :position, :only_integer => true, :if => :requires_position?
-
1
validates_uniqueness_of :position, :scope => :batch_id, :if => :need_to_check_position?
-
-
# Each request can only belong to one batch.
-
1
validates_uniqueness_of :request_id, :message => '%{value} is already in a batch.'
-
1
before_validation(:if => :requires_position?) { |record| record.position ||= (record.batch.batch_requests.map(&:position).compact.max || 0) + 1 }
-
-
1
def move_to_position!(position)
-
update_attributes!(:sorting_requests_within_batch => true, :position => position)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class BindingKitBarcodeTask < Task
-
1
class BindingKitBarcodeData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && BindingKitBarcodeData.new(request)
-
end
-
-
1
def partial
-
"binding_kit_barcode_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_binding_kit_barcode_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_binding_kit_barcode_task(self, params)
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent < ActiveRecord::Base
-
-
1
EVENT_JSON_ROOT = 'event'
-
1
UNKNOWN_USER_IDENTIFIER = 'UNKNOWN'
-
-
1
include Uuid::Uuidable
-
-
1
extend BroadcastEvent::SubjectHelpers::SubjectableClassMethods
-
1
extend BroadcastEvent::MetadataHelpers::MetadatableClassMethods
-
1
extend BroadcastEvent::RenderHelpers::RenderableClassMethods
-
-
1
belongs_to :seed, :polymorphic => true
-
1
belongs_to :user
-
1
validates_presence_of :seed
-
-
1
serialize :properties
-
1
self.inheritance_column = "sti_type"
-
-
1
def initialize(*args)
-
raise StandardError, 'BroadcastEvents can not be created directly' unless self.class < BroadcastEvent
-
super
-
end
-
-
# Prefer email, fall back to login if missing
-
1
def user_identifier
-
return UNKNOWN_USER_IDENTIFIER if user.nil? # User has probably been deleted
-
user.email.blank? ? user.login : user.email
-
end
-
-
# Returns an array of all subjects
-
1
def subjects
-
self.class.subject_associations.map do |sa|
-
sa.for(seed,self)
-
end.flatten
-
end
-
-
# Returns a hash of all metadata
-
1
def metadata
-
Hash[self.class.metadata_finders.map {|mf| mf.for(seed,self) } ]
-
end
-
-
1
def routing_key
-
"#{Rails.env}.event.#{event_type}.#{id}"
-
end
-
-
1
def json_root
-
EVENT_JSON_ROOT
-
end
-
-
1
def event_type
-
self.class.event_type
-
end
-
-
1
def self.set_event_type(event_type)
-
12
@event_type = event_type
-
end
-
-
1
def self.event_type
-
@event_type
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::AssetAudit < BroadcastEvent
-
-
1
seed_class AssetAudit
-
-
1
def event_type
-
seed.key
-
end
-
-
1
def user_identifier
-
return seed.created_by if user.nil?
-
user.email.blank? ? user.login : user.email
-
end
-
-
-
1
has_subject(:labware,:asset)
-
1
has_subjects(:sample) {|audit,e| audit.asset.contained_samples }
-
1
has_subjects(:stock_plate) {|audit,e| audit.asset.is_a?(Plate) ? audit.asset.original_stock_plates : [] }
-
1
has_subjects(:study) {|audit,e| audit.asset.studies }
-
-
1
has_metadata(:message,:message)
-
1
has_metadata(:witnessed_by,:witnessed_by)
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::Batched < BroadcastEvent
-
-
1
set_event_type 'batched'
-
-
# Triggered whenever a batch is created
-
-
# Subjects
-
# Plates/Tubes as Source Labware
-
# StockPlates
-
# Study
-
# Samples
-
-
# Metadata
-
# Request Options
-
# Pipeline
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::LibraryComplete < BroadcastEvent
-
-
1
set_event_type 'library_complete'
-
-
# Properties takes :order_id
-
-
1
seed_class MultiplexedLibraryTube
-
-
1
has_subject(:order) {|_,e| e.order }
-
1
has_subject(:study) {|_,e| e.order.study }
-
1
has_subject(:project) {|_,e| e.order.project }
-
1
has_subject(:submission) {|_,e| e.order.submission }
-
-
-
1
has_subjects(:library_source_labware,:library_source_plates)
-
-
1
has_subject(:multiplexed_library) {|tube,e| tube }
-
-
1
has_subjects(:stock_plate,:original_stock_plates)
-
1
has_subjects(:sample) do |tube,e|
-
tube.requests_as_target.for_event_notification_by_order(e.order).including_samples_from_source.map(&:samples).flatten
-
end
-
-
1
def order
-
@order ||= Order.find(properties[:order_id],:include=>[:study,:project,:submission])
-
end
-
-
1
has_metadata(:library_type) {|_,e| e.order.request_options['library_type'] }
-
1
has_metadata(:fragment_size_from) {|_,e| e.order.request_options['fragment_size_required_from'] }
-
1
has_metadata(:fragment_size_to) {|_,e| e.order.request_options['fragment_size_required_to'] }
-
1
has_metadata(:bait_library) {|_,e| e.order.request_options[:bait_library_name] }
-
-
1
has_metadata(:order_type) {|_,e| e.order.order_role.try(:role)||'UNKNOWN' }
-
1
has_metadata(:submission_template) {|_,e| e.order.template_name }
-
-
1
has_metadata(:team) {|tube,e| tube.team||'UNKNOWN'}
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::LibraryReStart < BroadcastEvent
-
-
# Almost identical to library start.
-
-
1
set_event_type 'library_re_start'
-
-
# Properties takes :order_id
-
-
1
seed_class Plate
-
-
1
has_subject(:order) {|_,e| e.order }
-
1
has_subject(:study) {|_,e| e.order.study }
-
1
has_subject(:project) {|_,e| e.order.project }
-
1
has_subject(:submission) {|_,e| e.order.submission }
-
-
-
1
has_subject(:library_source_labware,:source_plate)
-
-
1
has_subjects(:stock_plate,:original_stock_plates)
-
1
has_subjects(:sample) { |plate,e| plate.samples_in_order(e.properties[:order_id]) }
-
-
1
def order
-
@order ||= Order.find(properties[:order_id],:include=>[:study,:project,:submission])
-
end
-
-
1
has_metadata(:library_type) {|_,e| e.order.request_options['library_type'] }
-
1
has_metadata(:fragment_size_from) {|_,e| e.order.request_options['fragment_size_required_from'] }
-
1
has_metadata(:fragment_size_to) {|_,e| e.order.request_options['fragment_size_required_to'] }
-
1
has_metadata(:bait_library) {|_,e| e.order.request_options[:bait_library_name] }
-
-
1
has_metadata(:order_type) {|_,e| e.order.order_role.try(:role)||'UNKNOWN' }
-
1
has_metadata(:submission_template) {|_,e| e.order.template_name }
-
-
1
has_metadata(:team) {|plate,e| plate.team }
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::LibraryStart < BroadcastEvent
-
-
1
set_event_type 'library_start'
-
-
# Properties takes :order_id
-
-
1
seed_class Plate
-
-
1
has_subject(:order) {|_,e| e.order }
-
1
has_subject(:study) {|_,e| e.order.study }
-
1
has_subject(:project) {|_,e| e.order.project }
-
1
has_subject(:submission) {|_,e| e.order.submission }
-
-
-
1
has_subject(:library_source_labware,:source_plate)
-
-
1
has_subjects(:stock_plate,:original_stock_plates)
-
1
has_subjects(:sample) { |plate,e| plate.samples_in_order(e.properties[:order_id]) }
-
-
1
def order
-
@order ||= Order.find(properties[:order_id],:include=>[:study,:project,:submission])
-
end
-
-
1
has_metadata(:library_type) {|_,e| e.order.request_options['library_type'] }
-
1
has_metadata(:fragment_size_from) {|_,e| e.order.request_options['fragment_size_required_from'] }
-
1
has_metadata(:fragment_size_to) {|_,e| e.order.request_options['fragment_size_required_to'] }
-
1
has_metadata(:bait_library) {|_,e| e.order.request_options[:bait_library_name] }
-
-
1
has_metadata(:order_type) {|_,e| e.order.order_role.try(:role)||'UNKNOWN' }
-
1
has_metadata(:submission_template) {|_,e| e.order.template_name }
-
-
1
has_metadata(:team) {|plate,e| plate.team }
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module BroadcastEvent::MetadataHelpers
-
-
1
class SimpleMetadataFinder
-
1
attr_reader :name, :method
-
1
def initialize(name,method)
-
2
@name = name.to_s
-
2
@method = method
-
end
-
1
def for(seed,event)
-
[name,seed.send(method)]
-
end
-
end
-
-
1
class BlockMetadataFinder
-
1
attr_reader :name, :block
-
1
def initialize(name,&block)
-
38
@name = name.to_s
-
38
@block = block
-
end
-
1
def for(seed,event)
-
[name,block.call(seed,event)]
-
end
-
end
-
-
1
module MetadatableClassMethods
-
1
def has_metadata(key,method=nil,&block)
-
40
return metadata_finders << SimpleMetadataFinder.new(key,method) unless method.nil?
-
38
return metadata_finders << BlockMetadataFinder.new(key,&block) unless block.nil?
-
raise StandardError, "No block or method defined for #{key} on #{name}"
-
end
-
-
1
def metadata_finders
-
40
@metadata_finders ||= []
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::OrderMade < BroadcastEvent
-
-
1
set_event_type 'order_made'
-
-
# Properties takes :order_id
-
-
1
seed_class Order
-
-
# The seed itself can be a subject
-
1
seed_subject :order
-
1
has_subject(:study,:study)
-
1
has_subject(:project,:project)
-
1
has_subject(:submission,:submission)
-
-
1
has_subjects(:sample,:samples)
-
-
1
has_subjects(:order_source_plate) do |order,event|
-
event.plates
-
end
-
-
1
has_subjects(:order_source_tubes) {|order,event| order.assets.select {|a| a.is_a?(Tube) } }
-
-
1
has_subjects(:stock_plate) {|order,event| event.plates.map(&:original_stock_plates).flatten.uniq }
-
-
1
def plates
-
return @plates if @plates
-
wells = seed.assets.select {|a| a.is_a?(Well) }
-
return [] if wells.empty?
-
@plates = Plate.with_wells(wells)
-
end
-
-
-
1
has_metadata(:library_type) {|order,e| order.request_options['library_type'] }
-
1
has_metadata(:fragment_size_from) {|order,e| order.request_options['fragment_size_required_from'] }
-
1
has_metadata(:fragment_size_to) {|order,e| order.request_options['fragment_size_required_to'] }
-
1
has_metadata(:read_length) {|order,e| order.request_options[:read_length] }
-
1
has_metadata(:bait_library) {|order,e| order.request_options[:bait_library_name] }
-
-
1
has_metadata(:order_type) {|order,e| order.order_role.try(:role)||'UNKNOWN' }
-
1
has_metadata(:submission_template) {|order,e| order.template_name }
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::PlateLibraryComplete < BroadcastEvent
-
-
1
set_event_type 'library_complete'
-
-
# Properties takes :order_id
-
-
1
seed_class Plate
-
-
1
has_subject(:order) {|_,e| e.order }
-
1
has_subject(:study) {|_,e| e.order.study }
-
1
has_subject(:project) {|_,e| e.order.project }
-
1
has_subject(:submission) {|_,e| e.order.submission }
-
-
1
has_subject(:library_source_labware,:library_source_plates)
-
-
1
has_subjects(:stock_plate,:original_stock_plates)
-
1
has_subjects(:sample) { |plate,e| plate.samples_in_order_by_target(e.properties[:order_id]) }
-
-
1
def order
-
@order ||= Order.find(properties[:order_id],:include=>[:study,:project,:submission])
-
end
-
-
1
has_metadata(:library_type) {|_,e| e.order.request_options['library_type'] }
-
1
has_metadata(:fragment_size_from) {|_,e| e.order.request_options['fragment_size_required_from'] }
-
1
has_metadata(:fragment_size_to) {|_,e| e.order.request_options['fragment_size_required_to'] }
-
1
has_metadata(:bait_library) {|_,e| e.order.request_options[:bait_library_name] }
-
-
1
has_metadata(:order_type) {|_,e| e.order.order_role.try(:role)||'UNKNOWN' }
-
1
has_metadata(:submission_template) {|_,e| e.order.template_name }
-
-
1
has_metadata(:team) {|plate,e| plate.team }
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::PlateRegister < BroadcastEvent
-
-
1
set_event_type 'plate_register'
-
-
# Created when a plate is first registered in Sequencescape be it via plate creators,
-
# plate creation or through eg cherrypicking
-
-
1
seed_class PlateCreation
-
-
# Subjects
-
# Target Plate
-
# Source Plates
-
# Stock plates
-
# origin_plate
-
-
# Metadata
-
# Plate purpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::PlateTransfer < BroadcastEvent
-
-
1
set_event_type 'plate_transfer'
-
-
# Created when a plate receives material. Should be as cloase to the actual lab event as possible
-
# Eg AssetAudit/BedVerification in SM
-
# Robot start (State chage?) in Library world
-
-
# Subjects
-
# Target Plate
-
# Source Plates
-
# Stock plates
-
# origin_plate
-
-
# Metadata
-
# Plate purpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::Reception < BroadcastEvent
-
-
1
set_event_type 'reception'
-
-
# Triggered whenever a plate is scanned into a new location
-
-
# Subjects
-
# Plate
-
# StockPlates
-
-
# Metadata
-
# Location
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module BroadcastEvent::RenderHelpers
-
# Controls our render.
-
1
class Render
-
1
def self.to_hash(event)
-
{
-
:uuid => event.uuid,
-
:event_type => event.event_type,
-
:occured_at => event.created_at,
-
:user_identifier => event.user_identifier,
-
:subjects => event.subjects,
-
:metadata => event.metadata
-
}
-
end
-
end
-
-
1
module RenderableClassMethods
-
1
def render_class
-
Render
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module BroadcastEvent::SampleRegistered
-
# Hook into the existing system here
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::SequencingQc < BroadcastEvent
-
-
# Perform some magic to generate these dynamically using def self.event_type
-
1
set_event_type 'sequencing_qc_pass'
-
1
set_event_type 'sequencing_qc_fail'
-
-
# Broadcast when npg sends us the qc event:
-
# Study
-
# Samples
-
# Project
-
# Asset (Tube or strip tube)
-
# Stock Plates
-
# Source Plates (Ie Cherrypicked)
-
-
# Metadata
-
# Read length
-
# Pipeline
-
# Team (Via request type)
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class BroadcastEvent::SequencingStart < BroadcastEvent
-
-
1
set_event_type 'sequencing_start'
-
-
1
seed_class Batch
-
-
# Broadcast when a sequencing request starts:
-
1
has_subjects(:sequencing_source_labware,:source_labware)
-
1
has_subjects(:study,:studies)
-
1
has_subjects(:project,:projects)
-
1
has_subjects(:stock_plate) {|batch,e| batch.source_labware.map(&:original_stock_plates).flatten.uniq }
-
1
has_subjects(:library_source_labware) {|batch,e| batch.source_labware.map(&:library_source_plates).flatten.uniq }
-
1
has_subjects(:sample,:samples)
-
-
# Metadata
-
1
has_metadata(:read_length) {|batch,e| batch.requests.first.request_metadata.read_length }
-
1
has_metadata(:pipeline) {|batch,e| batch.pipeline.name }
-
1
has_metadata(:team) {|batch,e| batch.requests.first.product_line}
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module BroadcastEvent::SubjectHelpers
-
-
class Subject
-
-
attr_reader :target, :role_type
-
-
def initialize(name,target)
-
@role_type = name.to_s
-
@target = target
-
end
-
-
def json_fields
-
[:friendly_name,:uuid,:subject_type,:role_type]
-
end
-
-
def as_json(*args)
-
Hash[json_fields.map {|f| [f,send(f)] }]
-
end
-
1
-
delegate :friendly_name, :uuid, :subject_type, :to => :target
-
end
-
-
1
module SimpleTargetLookup
-
1
def initialize(name,method)
-
17
@name = name
-
17
@method = method
-
end
-
-
1
def target_for(seed,event)
-
seed.send(method)
-
end
-
-
1
def self.included(base)
-
4
base.class_eval { attr_reader :name, :method }
-
end
-
end
-
-
1
module BlockTargetLookup
-
1
def initialize(name,&block)
-
29
@name = name
-
29
@block = block
-
end
-
-
1
def target_for(seed,event)
-
block.call(seed,event)
-
end
-
-
1
def self.included(base)
-
4
base.class_eval { attr_reader :name, :block }
-
end
-
end
-
-
1
module SingleTarget
-
1
def for(seed,event)
-
Subject.new(name,target_for(seed,event))
-
end
-
end
-
-
1
module MultiTarget
-
1
def for(seed,event)
-
target_for(seed,event).map {|t| Subject.new(name,t) }
-
end
-
end
-
-
1
class SeedSubjectAssociation
-
-
1
attr_reader :name
-
-
1
def initialize(name)
-
1
@name = name
-
end
-
1
def for(seed,event)
-
Subject.new(name,seed)
-
end
-
end
-
-
1
class SimpleSingleSubjectAssociation
-
1
include SimpleTargetLookup
-
1
include SingleTarget
-
end
-
-
1
class SimpleManySubjectAssociation
-
1
include SimpleTargetLookup
-
1
include MultiTarget
-
end
-
-
1
class BlockSingleSubjectAssociation
-
1
include BlockTargetLookup
-
1
include SingleTarget
-
end
-
-
1
class BlockManySubjectAssociation
-
1
include BlockTargetLookup
-
1
include MultiTarget
-
end
-
-
1
module SubjectableClassMethods
-
# The class expected to seed the messenger
-
1
def seed_class(seed_class)
-
8
@seed_class = seed_class
-
end
-
-
# The role type that will identify the seed (if applicable)
-
1
def seed_subject(role_type)
-
1
subject_associations << SeedSubjectAssociation.new(role_type)
-
end
-
-
# Defines a new subject, specifies the role type and either a method on the seed that will return
-
# the subject, or a block that gets passed the seed, and will return the subject.
-
1
def has_subject(role_type,method=nil,&block)
-
24
return subject_associations << SimpleSingleSubjectAssociation.new(role_type,method) unless method.nil?
-
17
return subject_associations << BlockSingleSubjectAssociation.new(role_type,&block) unless block.nil?
-
raise StandardError, "No block or method defined for #{role_type} on #{name}"
-
end
-
-
# Used when you explicitly expect to receive more than one subject
-
1
def has_subjects(role_type,method=nil,&block)
-
22
return subject_associations << SimpleManySubjectAssociation.new(role_type,method) unless method.nil?
-
12
return subject_associations << BlockManySubjectAssociation.new(role_type,&block) unless block.nil?
-
raise StandardError, "No block or method defined for #{role_type} on #{name}"
-
end
-
-
1
def subject_associations
-
47
@subject_associations ||= []
-
end
-
end
-
-
1
module Subjectable
-
-
1
def self.included(base)
-
base.class.extend SubjectableClassMethods
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class BudgetDivision < ActiveRecord::Base
-
1
extend Attributable::Association::Target
-
-
1
validates_presence_of :name
-
1
has_many :project
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :message => "of budget division already present in database"
-
-
-
-
1
module Associations
-
1
def self.included(base)
-
1
base.validates_presence_of :budget_division_id
-
1
base.belongs_to :budget_division
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class ActiveRecord::Base
-
1
class << self
-
1
def find_by_id_or_name!(id, name)
-
return find(id) unless id.blank?
-
raise StandardError, "Must specify at least ID or name" if name.blank?
-
find_by_name!(name)
-
end
-
-
1
def find_by_id_or_name(id, name)
-
return find(id) unless id.blank?
-
raise StandardError, "Must specify at least ID or name" if name.blank?
-
find_by_name(name)
-
end
-
-
1
def find_all_by_id_or_name!(ids, names)
-
return Array(find(*ids)) unless ids.blank?
-
raise StandardError, "Must specify at least an ID or a name" if names.blank?
-
find_all_by_name(names).tap do |found|
-
missing = names - found.map(&:name)
-
raise ActiveRecord::RecordNotFound, "Could not find #{self.name} with names #{missing.inspect}" unless missing.blank?
-
end
-
end
-
end
-
end
-
-
1
class Array
-
1
def comma_separate_field_list(*fields)
-
field_list(*fields).join(',')
-
end
-
-
1
def comma_separate_field_list_for_display(*fields)
-
field_list(*fields).join(', ')
-
end
-
-
1
def field_list(*fields)
-
map { |row| fields.map { |field| row[field] } }.flatten.delete_if(&:blank?)
-
end
-
end
-
-
1
class BulkSubmission
-
1
include ActiveModel::AttributeMethods
-
1
include ActiveModel::Validations
-
1
include ActiveModel::Conversion
-
1
extend ActiveModel::Naming
-
-
1
attr_accessor :spreadsheet
-
1
define_attribute_methods [:spreadsheet]
-
-
1
validates_presence_of :spreadsheet
-
1
validate :process_file
-
-
1
def persisted?; false; end
-
1
def id; nil; end
-
-
1
def initialize(attrs={})
-
self.spreadsheet = attrs[:spreadsheet]
-
end
-
-
1
include ManifestUtil
-
-
1
def process_file
-
# Slightly inelegant file-type checking
-
#TODO (jr) Find a better way of verifying the CSV file?
-
unless spreadsheet.blank?
-
if spreadsheet.size == 0
-
errors.add(:spreadsheet, "The supplied file was empty")
-
else
-
if /^.*\.csv$/.match(spreadsheet.original_filename)
-
process
-
else
-
errors.add(:spreadsheet, "The supplied file was not a CSV file")
-
end
-
end
-
end
-
rescue CSV::MalformedCSVError
-
errors.add(:spreadsheet, "The supplied file was not a valid CSV file (try opening it with MS Excel)")
-
end
-
-
1
def headers
-
@headers ||= filter_end_of_header(@csv_rows.fetch(header_index)) unless header_index.nil?
-
end
-
1
private :headers
-
-
1
def csv_data_rows
-
@csv_rows.slice(header_index+1...@csv_rows.length)
-
end
-
1
private :csv_data_rows
-
-
1
def header_index
-
@header_index ||= @csv_rows.each_with_index do |row, index|
-
next if index == 0 && row[0] == "This row is guidance only"
-
return index if header_row?(row)
-
end
-
# We've got through all rows without finding a header
-
errors.add(:spreadsheet, "The supplied file does not contain a valid header row (try downloading a template)")
-
nil
-
end
-
1
private :header_index
-
-
1
def start_row
-
header_index + 2
-
end
-
1
private :start_row
-
-
1
def header_row?(row)
-
row.each {|col| col.try(:downcase!)}
-
(row & COMMON_FIELDS).length > 0
-
end
-
1
private :header_row?
-
-
1
def valid_header?
-
return false if headers.nil?
-
return true if headers.include? "submission name"
-
errors.add :spreadsheet, "You submitted an incompatible spreadsheet. Please ensure your spreadsheet contains the 'submission name' column"
-
false
-
end
-
-
1
def max_priority(orders)
-
orders.inject(0) do |max,order|
-
priority = Submission::Priorities.priorities.index(order['priority'])||order['priority'].to_i
-
priority > max ? priority.to_i : max
-
end
-
end
-
1
private :max_priority
-
-
1
def spreadsheet_valid?
-
valid_header?
-
errors.count == 0
-
end
-
1
private :spreadsheet_valid?
-
-
1
def process
-
# Store the details of the successful submissions so the user can be presented with a summary
-
@submission_ids = []
-
@completed_submissions = {}
-
@csv_rows = CSV.parse(spreadsheet.read)
-
-
if spreadsheet_valid?
-
submission_details = submission_structure
-
-
raise ActiveRecord::RecordInvalid, self if self.errors.count > 0
-
# Within a single transaction process each of the rows of the CSV file as a separate submission. Any name
-
# fields need to be mapped to IDs, and the 'assets' field needs to be split up and processed if present.
-
ActiveRecord::Base.transaction do
-
submission_details.each do |submissions|
-
submissions.each do |submission_name,orders|
-
user = User.find_by_login(orders.first['user login'])
-
if user.nil?
-
errors.add :spreadsheet, orders.first["user login"].nil? ? "No user specified for #{submission_name}" : "Cannot find user #{orders.first["user login"].inspect}"
-
next
-
end
-
-
begin
-
submission = Submission.create!(:name=>submission_name, :user => user, :orders => orders.map(&method(:prepare_order)).compact, :priority=>max_priority(orders))
-
submission.built!
-
# Collect successful submissions
-
@submission_ids << submission.id
-
@completed_submissions[submission.id] = "Submission #{submission.id} built (#{submission.orders.count} orders)"
-
rescue Submission::ProjectValidation::Error => exception
-
errors.add :spreadsheet, "There was an issue with a project: #{exception.message}"
-
end
-
end
-
end
-
-
# If there are any errors then the transaction needs to be rolled back.
-
raise ActiveRecord::Rollback if errors.count > 0
-
end
-
-
end
-
end #process
-
-
1
COMMON_FIELDS = [
-
# Needed to construct the submission ...
-
'template name',
-
'study id', 'study name',
-
'project id', 'project name', 'submission name',
-
'user login',
-
-
# Needed to identify the assets and what happens to them ...
-
'asset group id', 'asset group name',
-
'fragment size from', 'fragment size to',
-
'read length',
-
'library type',
-
'bait library', 'bait library name',
-
'comments',
-
'number of lanes',
-
'pre-capture plex level',
-
'pre-capture group',
-
'gigabases expected',
-
'priority'
-
]
-
-
1
ALIAS_FIELDS = {
-
'plate barcode' => 'barcode',
-
'tube barcode' => 'barcode'
-
}
-
-
1
def translate(header)
-
ALIAS_FIELDS[header]||header
-
end
-
-
1
def validate_entry(header,pos,row,index)
-
return [translate(header), row[pos].try(:strip)] unless header.nil? && row[pos].present?
-
errors.add(:spreadsheet, "Row #{index}, column #{pos+1} contains data but no heading.")
-
end
-
1
private :validate_entry
-
-
# Process CSV into a structure
-
# this creates an array containing a hash for each distinct "submission name"
-
# "submission name" => array of orders
-
# where each order is a hash of headers to values (grouped by "asset group name")
-
1
def submission_structure
-
Hash.new {|h,i| h[i] = Array.new}.tap do |submission|
-
csv_data_rows.each_with_index do |row, index|
-
next if row.all?(&:nil?)
-
details = Hash[headers.each_with_index.map { |header, pos| validate_entry(header,pos,row,index+start_row) }].merge('row' => index+start_row)
-
submission[details['submission name']] << details
-
end
-
end.map do |submission_name, rows|
-
order = rows.group_by do |details|
-
details["asset group name"]
-
end.map do |group_name, rows|
-
-
Hash[shared_options!(rows)].tap do |details|
-
details['rows'] = rows.comma_separate_field_list_for_display('row')
-
details['asset ids'] = rows.field_list('asset id', 'asset ids')
-
details['asset names'] = rows.field_list('asset name', 'asset names')
-
details['plate well'] = rows.field_list('plate well')
-
details['barcode'] = rows.field_list('barcode')
-
end.delete_if { |_,v| v.blank? }
-
-
end
-
Hash[submission_name, order]
-
end
-
end
-
-
-
1
def shared_options!(rows)
-
# Builds an array of the common fields. Raises and exception if the fields are inconsistent
-
COMMON_FIELDS.map do |field|
-
option = rows.map {|r| r[field] }.uniq
-
self.errors.add(:spreadsheet, "Column, #{field}, should be identical for all requests in asset group #{rows.first['asset group name']}") if option.count > 1
-
[field, option.first]
-
end
-
end
-
-
# Returns an order for the given details
-
1
def prepare_order(details)
-
begin
-
# Retrieve common attributes
-
study = Study.find_by_id_or_name!(details['study id'], details['study name'])
-
project = Project.find_by_id_or_name!(details['project id'], details['project name'])
-
user = User.find_by_login(details['user login']) or raise StandardError, "Cannot find user #{details['user login'].inspect}"
-
-
# The order attributes are initially
-
attributes = {
-
:study => study,
-
:project => project,
-
:user => user,
-
:comments => details['comments'],
-
:request_options => {
-
:read_length => details['read length']
-
},
-
:pre_cap_group => details['pre-capture group']
-
}
-
-
attributes[:request_options]['library_type'] = details['library type'] unless details['library type'].blank?
-
attributes[:request_options]['fragment_size_required_from'] = details['fragment size from'] unless details['fragment size from'].blank?
-
attributes[:request_options]['fragment_size_required_to'] = details['fragment size to'] unless details['fragment size to'].blank?
-
attributes[:request_options][:bait_library_name] = details['bait library name'] unless details['bait library name'].blank?
-
attributes[:request_options][:bait_library_name] ||= details['bait library'] unless details['bait library'].blank?
-
attributes[:request_options]['pre_capture_plex_level'] = details['pre-capture plex level'] unless details['pre-capture plex level'].blank?
-
attributes[:request_options]['gigabases_expected'] = details['gigabases expected'] unless details['gigabases expected'].blank?
-
attributes[:request_options][:multiplier] = {}
-
-
# Deal with the asset group: either it's one we should be loading, or one we should be creating.
-
-
-
attributes[:asset_group] = study.asset_groups.find_by_id_or_name(details['asset group id'], details['asset group name'])
-
attributes[:asset_group_name] = details['asset group name'] if attributes[:asset_group].nil?
-
-
-
##
-
# We go ahead and find our assets regardless of whether we have an asset group.
-
# While this takes longer, it helps to detect cases where an asset group name has been
-
# reused. This is a common cause of submission problems.
-
-
# Locate either the assets by name or ID, or find the plate and it's well
-
if is_plate?(details)
-
-
found_assets = find_wells_for!(details)
-
# We've probably got a tube
-
elsif is_tube?(details)
-
-
found_assets = find_tubes_for!(details)
-
-
else
-
-
asset_ids, asset_names = details.fetch('asset ids', ''), details.fetch('asset names', '')
-
if attributes[:asset_group] && asset_ids.blank? && asset_names.blank?
-
found_assets = []
-
else
-
found_assets = Asset.find_all_by_id_or_name!(asset_ids, asset_names).uniq
-
end
-
-
assets_found, expecting = found_assets.map { |asset| "#{asset.name}(#{asset.id})" }, asset_ids.size + asset_names.size
-
raise StandardError, "Too few assets found for #{details['rows']}: #{assets_found.inspect}" if assets_found.size < expecting
-
raise StandardError, "Too many assets found for #{details['rows']}: #{assets_found.inspect}" if assets_found.size > expecting
-
-
end
-
-
-
if attributes[:asset_group].nil?
-
assets_not_in_study = found_assets.select { |asset| not asset.aliquots.map(&:sample).map(&:studies).flatten.uniq.include?(study) }
-
raise StandardError, "Assets not in study #{study.name.inspect} for #{details['rows']}: #{assets_not_in_study.map(&:display_name).inspect}" unless assets_not_in_study.empty?
-
attributes[:assets] = found_assets
-
else
-
raise StandardError, "Asset Group '#{attributes[:asset_group].name}' contains different assets to those you specified. You may be reusing an asset group name" if found_assets.present? && found_assets != attributes[:asset_group].assets
-
end
-
-
# Create the order. Ensure that the number of lanes is correctly set.
-
sub_template = find_template(details['template name'])
-
number_of_lanes = details.fetch('number of lanes', 1).to_i
-
-
sub_template.new_order(attributes).tap do |new_order|
-
new_order.request_type_multiplier do |multiplexed_request_type_id|
-
new_order.request_options[:multiplier][multiplexed_request_type_id] = number_of_lanes
-
end
-
end
-
rescue => exception
-
errors.add :spreadsheet, "There was a problem on row(s) #{details['rows']}: #{exception.message}"
-
nil
-
end
-
end
-
-
1
def is_plate?(details)
-
details['barcode'].present? and details['plate well'].present?
-
end
-
-
1
def is_tube?(details)
-
details['barcode'].present? and details['plate well'].blank?
-
end
-
-
1
def find_wells_for!(details)
-
barcodes, well_list = details['barcode'], details['plate well']
-
self.errors.add(:spreadsheet, "You can only specify one plate per asset group") unless barcodes.uniq.one?
-
barcode = barcodes.first
-
-
match = /^([A-Z]{2})(\d+)[A-Z]$/.match(barcode) or raise StandardError, "Plate Barcode should be human readable (e.g. DN111111K)"
-
prefix = BarcodePrefix.find_by_prefix(match[1]) or raise StandardError, "Cannot find barcode prefix #{match[1].inspect} for #{details['rows']}"
-
plate = Plate.find_by_barcode_prefix_id_and_barcode(prefix.id, match[2]) or raise StandardError, "Cannot find plate with barcode #{barcode} for #{details['rows']}"
-
-
well_locations = well_list.map(&:strip)
-
wells = plate.wells.located_at(well_locations)
-
raise StandardError, "Too few wells found for #{details['rows']}: #{wells.map(&:map).map(&:description).inspect}" if wells.length != well_locations.size
-
wells
-
end
-
-
1
def find_tubes_for!(details)
-
details['barcode'].map do |barcode|
-
match = /^([A-Z]{2})(\d+)[A-Z]$/.match(barcode) or raise StandardError, "Tube Barcode should be human readable (e.g. NT2P)"
-
prefix = BarcodePrefix.find_by_prefix(match[1]) or raise StandardError, "Cannot find barcode prefix #{match[1].inspect} for #{details['rows']}"
-
plate = Tube.find_by_barcode_prefix_id_and_barcode(prefix.id, match[2]) or raise StandardError, "Cannot find tube with barcode #{barcode} for #{details['rows']}."
-
end
-
end
-
-
# Returns the SubmissionTemplate and checks that it is valid
-
1
def find_template(template_name)
-
template = SubmissionTemplate.find_by_name(template_name) or raise StandardError, "Cannot find template #{template_name}"
-
raise(StandardError, "Template: '#{template_name}' is deprecated and no longer in use.") unless template.visible
-
template
-
end
-
-
# This is used to present a list of successes
-
1
def completed_submissions
-
return @submission_ids, @completed_submissions
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class BulkTransfer < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
-
1
has_many :transfers
-
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
1
after_create :build_transfers!
-
-
1
attr_accessor :well_transfers
-
-
1
def build_transfers!
-
ActiveRecord::Base.transaction do
-
each_transfer do |source,destination,transfers|
-
Transfer::BetweenPlates.create!(
-
:source=>source,
-
:destination=>destination,
-
:user => user,
-
:transfers => transfers,
-
:bulk_transfer_id => self.id
-
)
-
end
-
end
-
end
-
1
private :build_transfers!
-
-
1
def each_transfer
-
well_transfers.group_by { |tf| [tf["source_uuid"],tf["destination_uuid"]] }.each do |source_dest, all_transfers|
-
transfers = Hash.new {|h,i| h[i]=[] }
-
all_transfers.each {|t| transfers[t["source_location"]] << t["destination_location"] }
-
-
source = Uuid.find_by_external_id(source_dest.first).resource
-
destination = Uuid.find_by_external_id(source_dest.last).resource
-
errors.add(:source, 'is not a plate') unless source.is_a?(Plate)
-
errors.add(:destination, 'is not a plate') unless destination.is_a?(Plate)
-
raise ActiveRecord::RecordInvalid, self if errors.count > 0
-
yield(source,destination,transfers)
-
end
-
end
-
1
private :each_transfer
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module ChangeTagException
-
1
class MissingTag < Exception
-
end
-
1
class MissingLibraryTube < Exception
-
end
-
end
-
-
1
class ChangeTag
-
1
attr_accessor :library_tubes
-
1
attr_accessor :library_tube_ids
-
-
1
def initialize(options = {})
-
parse_library_tube_ids(options[:library_tube_ids])
-
lookup_library_tubes
-
end
-
-
1
def validate!
-
raise ChangeTagException::MissingLibraryTube if library_tubes.include?(nil)
-
tubes_have_tags!
-
end
-
-
1
def self.update_tags(library_tubes_to_tags)
-
ActiveRecord::Base.transaction do
-
library_tubes_to_tags.each do |library_tube_id, tag_id|
-
tube = LibraryTube.find(library_tube_id)
-
new_tag = Tag.find(tag_id)
-
tube.aliquots.first.descendants(true).map do |aliquot|
-
aliquot.update_attributes!(:tag => new_tag)
-
end
-
end
-
end
-
end
-
-
1
protected
-
1
def parse_library_tube_ids(library_tube_ids_string)
-
@library_tube_ids = []
-
library_tube_ids_string.scan(/\d+/).each do |library_tube_id|
-
library_tube_ids << library_tube_id
-
end
-
end
-
-
1
def lookup_library_tubes
-
@library_tubes = []
-
library_tube_ids.each do |asset_id|
-
library_tubes << asset_from_id(asset_id)
-
end
-
end
-
-
1
def asset_from_id(asset_id)
-
Asset.find_by_id(asset_id) || Asset.find_by_barcode(asset_id)
-
end
-
-
1
def tubes_have_tags!
-
library_tubes.each do |library_tube|
-
raise ChangeTagException::MissingTag if library_tube.get_tag.nil?
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module Cherrypick
-
# Various types of error that can occur during cherrypicking
-
1
Error = Class.new(StandardError)
-
1
VolumeError = Class.new(Error)
-
1
ConcentrationError = Class.new(Error)
-
1
AmountError = Class.new(Error)
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014 Genome Research Ltd.
-
class Cherrypick::Strategy
-
PickFailureError = Class.new(StandardError)
-
-
# Classes inside this module represent filters that can be combined to reduce the set of plexes
-
# for a cherrypick to the optimum selection.
-
module Filter
-
# Shortens any plexes over the available space of the plate to fit.
-
class ShortenPlexesToFit
-
def call(plexes, current_plate)
-
plexes.map { |p| p.slice(0, current_plate.available) }
-
end
-
end
-
-
# Ensures that the plate is not overflowed by any of the plexes.
-
class ByOverflow
-
def call(plexes, current_plate)
-
plexes.select { |plex| (plex.size + current_plate.used) <= current_plate.size }
-
end
-
end
-
-
# Ensures that the plexes do not overflow the dimension of the plate when added.
-
class ByEmptySpaceUsage
-
def call(plexes, current_plate)
-
return plexes if current_plate.overlap.zero?
-
-
plexes.select do |plex|
-
empty_space_after_addition = (plex.size + current_plate.overlap) % current_plate.dimension
-
empty_space_after_addition <= ((plex.size >= current_plate.dimension) ? 0 : current_plate.dimension)
-
end
-
end
-
end
-
-
# Orders the plexes by the optimum fitting
-
class BestFit
-
def call(plexes, current_plate)
-
comparator = ->(l,r) { r.size <=> l.size }
-
comparator = lambda do |left, right|
-
left_fill, right_fill = current_plate.space_after_adding(left), current_plate.space_after_adding(right)
-
sorted_fill = left_fill <=> right_fill
-
sorted_fill = right.size <=> left.size if sorted_fill.zero?
-
sorted_fill
-
end unless current_plate.overlap.zero?
-
-
plexes.sort(&comparator)
-
end
-
end
-
-
# Orders the plexes such that plexes with the same species as the plate come first, ensuring that
-
# the plate has species closely packed. We're going to assume that if the well has multiple samples
-
# in it, then any of those species is a good choice. Ordering is maintained within plexes, that is,
-
# appropriate plexes bubble to the top but maintain their relative ordering; this means filters that
-
# apply an ordering can be used before this.
-
class BySpecies
-
def call(plexes, current_plate)
-
species = current_plate.species
-
return plexes if species.empty?
-
-
plexes.each_with_index.sort do |(left,left_index), (right,right_index)|
-
left_species, right_species = species_for_plex(left), species_for_plex(right)
-
left_in, right_in = species & left_species, species & right_species
-
case
-
when left_in.empty? && right_in.empty? then left_index <=> right_index # No match (maintain order)
-
when !left_in.empty? && !right_in.empty? then left_index <=> right_index # Both match (maintain order)
-
when !left_in.empty? then -1 # Left better
-
else 1 # Right better
-
end
-
end.map(&:first)
-
end
-
-
def species_for_plex(plex)
-
plex.map(&:species).flatten.uniq.sort
-
end
-
private :species_for_plex
-
end
-
-
# Ensures that all of the plexes are internally ordered based on their position in the submission
-
class InternallyOrderPlexBySubmission
-
def call(plexes, current_plate)
-
plexes.map do |plex|
-
plex.sort_by(&:index_in_submission)
-
end
-
end
-
end
-
-
class InRowOrder
-
def call(plexes, current_plate)
-
plexes.map do |plex|
-
plex.sort_by(&:row_index)
-
end
-
end
-
end
-
-
-
class InColumnOrder
-
def call(plexes, current_plate)
-
plexes.map do |plex|
-
plex.sort_by(&:column_index)
-
end
-
end
-
end
-
end
-
-
class PickPlate
-
def initialize(purpose, filled = 0, species = [])
-
@purpose, @wells, @species = purpose, [Cherrypick::Strategy::Empty] * filled, species
-
end
-
-
delegate :size, :cherrypick_direction, :to => :@purpose
-
-
# This is the size of the plate in the dimension in which we cherrypick.
-
def dimension
-
@dimension ||= Map::Coordinate.send(:"plate_#{cherrypick_direction == 'row' ? 'width' : 'length'}", size)
-
end
-
-
def available
-
size - used
-
end
-
-
delegate :empty?, :inspect, :concat, :to => :@wells
-
-
def used
-
@wells.size
-
end
-
-
def space_after_adding(plex)
-
(available - plex.size) % dimension
-
end
-
-
def overlap
-
used % dimension
-
end
-
-
def remainder
-
(dimension - overlap) % dimension
-
end
-
-
def species
-
@wells.map(&:species).reject(&:empty?).last || @species
-
end
-
-
def to_a
-
@wells.map(&:representation)
-
end
-
end
-
-
Empty = Object.new.tap do |empty|
-
class << empty
-
def barcode
-
nil
-
end
-
-
# This well really isn't present!
-
def present?
-
false
-
end
-
-
def representation
-
self
-
end
-
-
def species
-
[]
-
end
-
-
def inspect
-
'Empty'
-
end
-
-
def row_index
-
nil
-
end
-
-
def column_index
-
nil
-
end
-
end
-
end
-
-
class Full
-
def initialize(request)
-
@request = request
-
end
-
-
def submission_id
-
@submission_id ||= @request.submission_id
-
end
-
-
def index_in_submission
-
@index_in_submission ||= @request.submission.requests.index(@request)
-
end
-
-
def barcode
-
@barcode ||= @request.asset.plate.sanger_human_barcode
-
end
-
-
def representation
-
@request
-
end
-
-
def species
-
@request.asset.aliquots.map { |a| a.sample.sample_metadata.sample_common_name }
-
end
-
-
def row_index
-
@request.asset.map.row_order
-
end
-
-
def column_index
-
@request.asset.map.column_order
-
end
-
end
-
-
def initialize(purpose)
-
@purpose = purpose
-
end
-
1
-
delegate :cherrypick_filters, :to => :@purpose
-
1
private :cherrypick_filters
-
-
1
def pick(requests, robot, plate = nil)
-
_pick(requests.map(&Full.method(:new)), robot, wrap_plate(plate))
-
end
-
-
1
def create_empty_plate
-
PickPlate.new(@purpose)
-
end
-
1
private :create_empty_plate
-
-
# Given a, possibly nil, plate, create something that knows how to the pick of a plex will affect
-
# that plate. We assume that the space used on the specified plate is contiguous. Sometimes
-
# plates can be completely devoid of wells, in which case they can be treated as an empty plate
-
# too.
-
1
def wrap_plate(plate)
-
return create_empty_plate if plate.nil?
-
-
# Identify the last well (empty or not) on the plate as the point at which we start the pick
-
boundary_location = plate.wells.in_preferred_order.map { |w| w.map }.last or return create_empty_plate
-
boundary_index = plate.plate_purpose.well_locations.index(boundary_location) or
-
raise "Cannot find #{boundary_location.inspect} on #{plate.id}"
-
-
# Pick out the last full well of the plate as the species we're supposed to use
-
last_well, species = plate.wells.in_preferred_order.reject { |w| w.aliquots.empty? }.last, []
-
species = last_well.aliquots.map { |a| a.sample.sample_metadata.sample_common_name }.uniq.sort if last_well.present?
-
-
PickPlate.new(@purpose, boundary_index+1, species)
-
end
-
1
private :wrap_plate
-
-
1
def _pick(requests, robot, current_plate = create_empty_plate)
-
[].tap do |plate_picks|
-
previous_picked_plates = []
-
until requests.empty?
-
# Here we keep selecting plexes according to our core strategy. Should the selected plex violate
-
# the number of beds on the robot, then we simply discard it and try again. If there are no plexes
-
# that fit, then we give up on this plate and move on to a new one.
-
next_plex, pick_list = [], requests
-
until pick_list.empty?
-
next_plex, pick_list = choose_next_plex_from(pick_list, current_plate)
-
plates_in_this_plex = next_plex.map(&:barcode).compact
-
-
break if next_plex.empty? # No plex fits what we want
-
break if (plates_in_this_plex | previous_picked_plates).size <= robot.max_beds # Good set of plates
-
-
# Try to find a better option!
-
next_plex = []
-
end
-
-
# If we've managed to pick stuff then add it to the current plate, remove it from the pick list,
-
# and move on. If we've not picked anything then the current plate can be considered complete,
-
# in whatever fashion that is, so push it and move on.
-
if not next_plex.empty?
-
current_plate.concat(next_plex)
-
requests, previous_picked_plates = requests - next_plex, previous_picked_plates | next_plex.map(&:barcode).compact
-
elsif current_plate.empty?
-
raise PickFailureError
-
else
-
plate_picks.push(current_plate)
-
current_plate, previous_picked_plates = create_empty_plate, []
-
end
-
end
-
plate_picks.push(current_plate) unless current_plate.empty?
-
end.map(&:to_a)
-
end
-
1
private :_pick
-
-
# Picking the next plex involves applying each of the filters we have to the requests and then
-
# taking the first. The filters therefore reduce the set of requests, ordering them if desired,
-
# before we decide if there is a plex that's appropriate.
-
1
def choose_next_plex_from(requests, current_plate)
-
candidate_plexes = cherrypick_filters.map(&:new).inject(requests.group_by(&:submission_id).map(&:last)) do |plexes, filter|
-
filter.call(plexes, current_plate)
-
end
-
-
return [ [Cherrypick::Strategy::Empty] * current_plate.remainder, requests ] if candidate_plexes.empty?
-
[ candidate_plexes.first, requests - candidate_plexes.first ]
-
end
-
1
private :choose_next_plex_from
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Cherrypick::Task::PickByMicroLitre
-
1
def pick_by_micro_litre(*args)
-
options = args.extract_options!
-
cherrypick_wells_grouped_by_submission(*args, &create_micro_litre_picker(options))
-
end
-
-
1
def valid_params_for_micro_litre_pick?(options)
-
valid_float_param?(options[:micro_litre_volume_required])
-
end
-
1
private :valid_params_for_micro_litre_pick?
-
-
1
def create_micro_litre_picker(params)
-
volume = params[:micro_litre_volume_required].to_f
-
-
lambda do |well, _|
-
well.volume_to_cherrypick_by_micro_litre(volume)
-
end
-
end
-
1
private :create_micro_litre_picker
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Cherrypick::Task::PickByNanoGrams
-
1
def pick_by_nano_grams(*args)
-
options = args.extract_options!
-
cherrypick_wells_grouped_by_submission(*args, &create_nano_grams_picker(options))
-
end
-
-
1
def valid_params_for_nano_grams_pick?(options)
-
[options[:minimum_volume], options[:maximum_volume], options[:total_nano_grams]].all?(&method(:valid_float_param?)) or return false
-
options[:minimum_volume].to_f <= options[:maximum_volume].to_f
-
end
-
1
private :valid_params_for_nano_grams_pick?
-
-
1
def create_nano_grams_picker(params)
-
min_vol, max_vol, nano_grams = params[:minimum_volume].to_f, params[:maximum_volume].to_f, params[:total_nano_grams].to_f
-
robot = Robot.find(params[:robot_id])
-
robot_minimum_picking_volume = robot.minimum_volume
-
-
lambda do |well, request|
-
well.volume_to_cherrypick_by_nano_grams(min_vol, max_vol, nano_grams, request.asset, robot_minimum_picking_volume)
-
end
-
end
-
1
private :create_nano_grams_picker
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
module Cherrypick::Task::PickByNanoGramsPerMicroLitre
-
1
def pick_by_nano_grams_per_micro_litre(*args)
-
options = args.extract_options!
-
cherrypick_wells_grouped_by_submission(*args, &create_nano_grams_per_micro_litre_picker(options))
-
end
-
-
1
def valid_params_for_nano_grams_per_micro_litre_pick?(options)
-
[options[:volume_required], options[:concentration_required]].all?(&method(:valid_float_param?))
-
end
-
1
private :valid_params_for_nano_grams_per_micro_litre_pick?
-
-
1
def create_nano_grams_per_micro_litre_picker(params)
-
volume, concentration = params[:volume_required].to_f, params[:concentration_required].to_f
-
robot = Robot.find(params[:robot_id])
-
robot_minimum_picking_volume = robot.minimum_volume
-
-
lambda do |well, request|
-
well.volume_to_cherrypick_by_nano_grams_per_micro_litre(volume, concentration, request.asset.get_concentration, robot_minimum_picking_volume)
-
end
-
end
-
1
private :create_nano_grams_per_micro_litre_picker
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
module Cherrypick::Task::PickHelpers
-
1
def self.included(base)
-
2
base.class_eval do
-
2
include Cherrypick::Task::PickByNanoGramsPerMicroLitre
-
2
include Cherrypick::Task::PickByNanoGrams
-
2
include Cherrypick::Task::PickByMicroLitre
-
end
-
end
-
-
1
def cherrypick_wells_grouped_by_submission(requests, robot, purpose, &picker)
-
plate, purpose = nil, purpose
-
plate, purpose = purpose, purpose.plate_purpose if purpose.is_a?(Plate)
-
-
purpose.cherrypick_strategy.pick(requests, robot, plate).map do |wells|
-
wells_and_requests = wells.zip(purpose.well_locations.slice(0, wells.size)).map do |request, position|
-
if request.present?
-
well = request.target_asset
-
well.map = position
-
picker.call(well, request)
-
[ well, request ]
-
else
-
nil
-
end
-
end.compact
-
-
wells_and_requests.each { |well, request| well.well_attribute.save! ; well.save! ; request.pass! }
-
-
# Attach the wells to the existing partial plate, or to a new plate if we need to create
-
# one. After the partial plate has been attached to we automatically need a new plate.
-
plate ||= purpose.create!(:do_not_create_wells) do |plate|
-
plate.name = "Cherrypicked #{plate.barcode}"
-
end
-
plate.tap do |working_on|
-
working_on.wells.attach(wells_and_requests.map(&:first))
-
plate = nil
-
end
-
end
-
end
-
1
private :cherrypick_wells_grouped_by_submission
-
-
1
def valid_float_param?(input_value)
-
!input_value.blank? && (input_value.to_f > 0.0)
-
end
-
1
private :valid_float_param?
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
module Cherrypick::VolumeByMicroLitre
-
1
def volume_to_cherrypick_by_micro_litre(volume_required, robot_minimum_picking_volume=0.0)
-
robot_minimum_picking_volume||=0.0
-
check_inputs_to_volume_to_cherrypick_by_micro_litre!(volume_required)
-
-
volume_required = [volume_required, robot_minimum_picking_volume].max
-
-
volume_required.to_f.tap do |volume_to_pick|
-
well_attribute.current_volume = volume_required.to_f
-
well_attribute.requested_volume = volume_required.to_f
-
well_attribute.buffer_volume = 0
-
well_attribute.picked_volume = volume_to_pick
-
end
-
end
-
-
1
def check_inputs_to_volume_to_cherrypick_by_micro_litre!(volume_required)
-
raise Cherrypick::VolumeError, "Volume required (#{volume_required.inspect}) is invalid for cherrypicking by micro litre" if volume_required.blank? || volume_required.to_f <= 0.0
-
end
-
1
private :check_inputs_to_volume_to_cherrypick_by_micro_litre!
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
module Cherrypick::VolumeByNanoGrams
-
1
def check_inputs_to_volume_to_cherrypick_by_nano_grams!(minimum_volume, maximum_volume, target_ng, source_well)
-
raise "Source well not found" if source_well.nil?
-
-
raise Cherrypick::VolumeError, "Minimum volume (#{minimum_volume.inspect}) is invalid for cherrypick by nano grams" if minimum_volume.blank? || minimum_volume <= 0.0
-
raise Cherrypick::VolumeError, "Maximum volume (#{maximum_volume.inspect}) is invalid for cherrypick by nano grams" if maximum_volume.blank? || maximum_volume <= 0.0
-
raise Cherrypick::VolumeError, "Maximum volume (#{maximum_volume.inspect}) is less than minimum (#{minimum_volume.inspect})" if maximum_volume < minimum_volume
-
-
raise Cherrypick::AmountError, "Target nano grams (#{target_ng.inspect}) is invalid for cherrypick by nano grams" if target_ng.blank? || target_ng <= 0.0
-
-
source_concentration, source_volume = source_well.well_attribute.concentration, source_well.well_attribute.measured_volume
-
raise Cherrypick::VolumeError, "Missing measured volume for well #{source_well.display_name}(#{source_well.id})" if source_volume.blank? || source_volume <= 0.0
-
raise Cherrypick::ConcentrationError, "Missing measured concentration for well #{source_well.display_name}(#{source_well.id})" if source_concentration.blank?
-
raise Cherrypick::ConcentrationError, "Concentration is negative for well #{source_well.display_name}(#{source_well.id})" if source_concentration < 0.0
-
end
-
1
private :check_inputs_to_volume_to_cherrypick_by_nano_grams!
-
-
1
def volume_to_cherrypick_by_nano_grams(minimum_volume, maximum_volume, target_ng, source_well, robot_minimum_picking_volume=0.0)
-
robot_minimum_picking_volume||=0.0
-
check_inputs_to_volume_to_cherrypick_by_nano_grams!(minimum_volume, maximum_volume, target_ng, source_well)
-
-
source_concentration = source_well.well_attribute.concentration.to_f
-
source_volume = source_well.well_attribute.measured_volume.to_f
-
desired_volume = source_volume
-
unless source_concentration.zero?
-
desired_volume = [(target_ng.to_f/source_concentration), robot_minimum_picking_volume].max
-
end
-
requested_volume = [ source_volume, desired_volume ].min
-
buffer_volume = buffer_volume_required(minimum_volume, requested_volume, robot_minimum_picking_volume)
-
requested_volume = maximum_volume if requested_volume > maximum_volume
-
-
well_attribute.current_volume = minimum_volume
-
well_attribute.requested_volume = minimum_volume
-
well_attribute.picked_volume = requested_volume
-
well_attribute.buffer_volume = buffer_volume
-
-
requested_volume
-
end
-
-
1
def buffer_volume_required(minimum_volume, requested_volume, robot_minimum_picking_volume)
-
val = [minimum_volume - requested_volume, 0.0].max
-
if val > 0.0
-
val = [val, robot_minimum_picking_volume].max
-
end
-
val
-
end
-
1
private :buffer_volume_required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
module Cherrypick::VolumeByNanoGramsPerMicroLitre
-
1
def volume_to_cherrypick_by_nano_grams_per_micro_litre(volume_required,concentration_required,source_concentration,robot_minimum_picking_volume=0.0)
-
robot_minimum_picking_volume||=0
-
check_inputs_to_volume_to_cherrypick_by_nano_grams_per_micro_litre!(volume_required,concentration_required,source_concentration)
-
-
source_volume = well_attribute.current_volume
-
well_attribute.concentration = concentration_required
-
well_attribute.requested_volume = volume_required
-
well_attribute.current_volume = volume_required
-
-
volume_to_pick = [[volume_required, robot_minimum_picking_volume].max, source_volume].compact.min
-
buffer_volume = 0.0
-
unless source_concentration.zero?
-
volume_to_pick = [[volume_required, ((volume_required*concentration_required)/source_concentration) ].min, robot_minimum_picking_volume].max
-
volume_to_pick = [source_volume, volume_to_pick].compact.min
-
buffer_volume = buffer_volume_required(volume_required, volume_to_pick, robot_minimum_picking_volume)
-
end
-
-
well_attribute.picked_volume = volume_to_pick
-
well_attribute.buffer_volume = buffer_volume
-
-
volume_to_pick
-
end
-
-
1
def check_inputs_to_volume_to_cherrypick_by_nano_grams_per_micro_litre!(volume_required,concentration_required,source_concentration)
-
raise Cherrypick::VolumeError, "Volume required (#{volume_required.inspect}) is invalid for cherrypick by nano grams per micro litre" if volume_required.blank? || volume_required.to_f <= 0.0
-
raise Cherrypick::ConcentrationError, "Concentration required (#{concentration_required.inspect}) is invalid for cherrypick by nano grams per micro litre" if concentration_required.blank? || concentration_required.to_f <= 0.0
-
raise Cherrypick::ConcentrationError, "Source concentration (#{source_concentration.inspect}) is invalid for cherrypick by nano grams per micro litre" if source_concentration.blank? || source_concentration.to_f < 0.0
-
end
-
1
private :check_inputs_to_volume_to_cherrypick_by_nano_grams_per_micro_litre!
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class CherrypickForFluidigmRequest < CherrypickForPulldownRequest
-
-
1
has_metadata :as => Request do
-
1
belongs_to :target_purpose, :class_name => 'Purpose'
-
1
association(:target_purpose, :name)
-
1
validates_presence_of :target_purpose
-
end
-
-
1
def target_purpose
-
request_metadata.target_purpose
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class CherrypickForPulldownPipeline < CherrypickingPipeline
-
1
include Pipeline::InboxGroupedBySubmission
-
-
1
def display_next_pipeline?
-
true
-
end
-
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
1
def post_finish_batch(batch, user)
-
# Nothing, we don't want all the requests to be completed
-
end
-
-
1
def post_release_batch(batch, user)
-
batch.release_pending_requests()
-
end
-
-
1
def update_detached_request(batch, request)
-
# We do not need to do any of the default behaviour:
-
# 1. The requests should just be detached, not blocked
-
# 2. The assets are not removed because they are not considered unused
-
end
-
-
1
def all_requests_from_submissions_selected?(request_ids)
-
requests = Request.where(:id=>request_ids).includes(:submission).all
-
expected_requests = all_request_from_submissions_filtered_by_request_type(submissions_from_requests(requests),requests.first.request_type)
-
return true if requests.size == expected_requests.size
-
-
false
-
end
-
-
1
def all_request_from_submissions_filtered_by_request_type(submissions, request_type)
-
Request.find_all_by_submission_id(submissions.map(&:id), :conditions => ["request_type_id = #{request_type.id}"])
-
end
-
1
private :all_request_from_submissions_filtered_by_request_type
-
-
1
def submissions_from_requests(requests)
-
requests.map{ |request| request.submission }.uniq
-
end
-
1
private :submissions_from_requests
-
-
# Validates that the requests in the batch lead into the same pipeline.
-
1
def validation_of_requests(requests, &block)
-
super # Could throw, which means that the rest of this function does not get executed
-
-
yield('cannot be mixed across pulldown pipelines') if requests.map do |request|
-
request.submission.next_requests(request).map(&:request_type)
-
end.flatten.uniq.size > 1
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014,2015 Genome Research Ltd.
-
1
class CherrypickForPulldownRequest < TransferRequest
-
-
1
redefine_state_machine do
-
# The statemachine for transfer requests is more promiscuous than normal requests, as well
-
# as being more concise as it has less states.
-
1
aasm_column :state
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :failed, :enter => :on_failed
-
1
aasm_state :passed
-
1
aasm_state :cancelled, :enter => :on_cancelled
-
1
aasm_state :hold
-
1
aasm_initial_state :pending
-
-
1
aasm_event :hold do
-
1
transitions :to => :hold, :from => [ :pending ]
-
end
-
-
# State Machine events
-
1
aasm_event :start do
-
1
transitions :to => :started, :from => [:pending,:hold]
-
end
-
-
1
aasm_event :pass do
-
1
transitions :to => :passed, :from => [:pending, :started, :failed]
-
end
-
-
1
aasm_event :fail do
-
1
transitions :to => :failed, :from => [:pending, :started, :passed]
-
end
-
-
1
aasm_event :cancel do
-
1
transitions :to => :cancelled, :from => [:started, :passed]
-
end
-
-
1
aasm_event :cancel_before_started do
-
1
transitions :to => :cancelled, :from => [:pending,:hold]
-
end
-
-
1
aasm_event :submission_cancelled do
-
1
transitions :to => :cancelled, :from => [:pending, :cancelled]
-
end
-
-
1
aasm_event :detach do
-
1
transitions :to => :pending, :from => [:pending, :cancelled]
-
end
-
end
-
-
1
def on_failed
-
# Do nothing
-
end
-
-
1
alias_method :on_cancelled, :on_failed
-
-
1
def perform_transfer_of_contents
-
on_started # Ensures we set the study/project
-
end
-
1
private :perform_transfer_of_contents
-
-
1
after_create :build_stock_well_links
-
-
1
def build_stock_well_links
-
stock_wells = asset.plate.try(:plate_purpose).try(:can_be_considered_a_stock_plate?) ? [asset] : asset.stock_wells
-
target_asset.stock_wells.attach!(stock_wells)
-
end
-
1
private :build_stock_well_links
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class CherrypickGroupBySubmissionTask < Task
-
1
include Cherrypick::Task::PickHelpers
-
1
include Tasks::PlatePurposeBehavior
-
1
include Request::GroupingHelpers
-
-
1
class CherrypickGroupBySubmissionData < Task::RenderElement
-
1
alias_attribute :well, :asset
-
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && CherrypickGroupBySubmissionData.new(request)
-
end
-
-
1
def partial
-
"cherrypick_group_by_submission_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_cherrypick_group_by_submission_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_cherrypick_group_by_submission_task(self, params)
-
end
-
-
1
def valid_params?(options = {})
-
param_checker_for_pick = "valid_params_for_#{options[:cherrypick][:action]}_pick?"
-
respond_to?(param_checker_for_pick, true) ? send("valid_params_for_#{options[:cherrypick][:action]}_pick?", options) : false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
class CherrypickPipeline < CherrypickingPipeline
-
1
include Pipeline::InboxGroupedBySubmission
-
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
1
def post_finish_batch(batch, user)
-
# Nothing, we don't want all the requests to be completed
-
end
-
-
1
def post_release_batch(batch, user)
-
# stock wells
-
batch.requests.each do |request|
-
EventSender.send_pick_event(request.target_asset.id, request.target_asset.plate.purpose.name, "Pickup well #{request.asset.id}")
-
end
-
batch.release_pending_requests()
-
batch.output_plates.each(&:cherrypick_completed)
-
end
-
-
1
def update_detached_request(batch, request)
-
# We do not need to do any of the default behaviour:
-
# 1. The requests should just be detached, not blocked
-
# 2. The assets are not removed because they are not considered unused
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class CherrypickTask < Task
-
1
EMPTY_WELL = [0,"Empty",""]
-
1
TEMPLATE_EMPTY_WELL = [0,'---','']
-
-
1
def create_render_element(request)
-
end
-
-
1
class BatchWrapper
-
1
def initialize(owner, batch)
-
@owner, @batch, @control_added = owner, batch, false
-
end
-
-
1
def control_added?
-
@control_added
-
end
-
-
1
def create_control_request_view_details(&block)
-
# NOTE: 'sample' here is not a Sequencescape sample but a random selection from the wells.
-
@owner.send(:generate_control_request, ControlPlate.first.illumina_wells.sample).tap do |request|
-
@batch.requests << request
-
yield([request.id, request.asset.plate.barcode, request.asset.map.description])
-
@control_added = true
-
end
-
end
-
end
-
-
# An instance of this class represents the target plate being picked onto. It can have a template
-
# and be a partial plate, and so when wells are picked into it we need to ensure that we don't hit
-
# the template/partial wells.
-
1
class PickTarget
-
1
def self.for(plate_purpose)
-
cherrypick_direction = plate_purpose.nil? ? 'column' : plate_purpose.cherrypick_direction
-
const_get("by_#{cherrypick_direction}".classify)
-
end
-
-
1
def initialize(batch, template, asset_shape=nil, partial = nil)
-
@wells, @size, @batch, @shape = [], template.size, batch, asset_shape||AssetShape.default
-
initialize_already_occupied_wells_from(template, partial)
-
add_any_wells_from_template_or_partial(@wells)
-
end
-
-
# Deals with generating the pick plate by travelling in a row direction, so A1, A2, A3 ...
-
1
class ByRow < PickTarget
-
1
def well_position(wells)
-
(wells.size+1) > @size ? nil : wells.size+1
-
end
-
1
private :well_position
-
-
1
def completed_view
-
@wells.dup.tap do |wells|
-
complete(wells)
-
end.each_with_index.inject([]) do |wells, (well, index)|
-
wells.tap { wells[@shape.horizontal_to_vertical(index+1, @size)] = well }
-
end.compact
-
end
-
end
-
-
# Deals with generating the pick plate by travelling in a column direction, so A1, B1, C1 ...
-
1
class ByColumn < PickTarget
-
1
def well_position(wells)
-
@shape.vertical_to_horizontal(wells.size+1, @size)
-
end
-
1
private :well_position
-
-
1
def completed_view
-
@wells.dup.tap { |wells| complete(wells) }
-
end
-
end
-
-
# Deals with generating the pick plate by travelling in an interlaced column direction, so A1, C1, E1 ...
-
1
class ByInterlacedColumn < PickTarget
-
1
def well_position(wells)
-
@shape.interlaced_vertical_to_horizontal(wells.size+1, @size)
-
end
-
1
private :well_position
-
-
1
def completed_view
-
@wells.dup.tap do |wells|
-
complete(wells)
-
end.each_with_index.inject([]) do |wells, (well, index)|
-
wells.tap { wells[@shape.vertical_to_interlaced_vertical(index+1, @size)] = well }
-
end.compact
-
end
-
end
-
-
1
def empty?
-
@wells.empty?
-
end
-
-
1
def full?
-
@wells.size == @size
-
end
-
-
1
def push(request_id, plate_barcode, well_location)
-
@wells << [request_id, plate_barcode, well_location]
-
add_any_wells_from_template_or_partial(@wells)
-
self
-
end
-
-
# Completes the given well array such that it looks like the plate has been completely picked.
-
1
def complete(wells)
-
until wells.size >= @size
-
add_empty_well(wells)
-
add_any_wells_from_template_or_partial(wells)
-
end
-
end
-
1
private :complete
-
-
# Determines the wells that are already occupied on the template or the partial plate. This is
-
# then used in add_any_wells_from_template_or_partial to fill them in as wells are added by the
-
# pick.
-
1
def initialize_already_occupied_wells_from(template, partial)
-
@used_wells = {}.tap do |wells|
-
[partial, template].compact.each do |plate|
-
plate.wells.each { |w| wells[w.map.horizontal_plate_position] = w.map.description }
-
end
-
end
-
-
@control_well_required = template.control_well? && (partial.nil? || !partial.control_well_exists?)
-
end
-
1
private :initialize_already_occupied_wells_from
-
-
# Every time a well is added to the pick we need to make sure that the template and partial are
-
# checked to see if subsequent wells are already taken. In other words, after calling this method
-
# the next position on the pick plate is known to be empty.
-
1
def add_any_wells_from_template_or_partial(wells)
-
wells << CherrypickTask::TEMPLATE_EMPTY_WELL until wells.size >= @size or @used_wells[well_position(wells)].nil?
-
return unless @control_well_required and wells.size == (@size-1)
-
-
# Control well is always in the bottom right corner of the plate
-
@batch.create_control_request_view_details do |control_request_view|
-
wells << control_request_view
-
@control_well_required = false
-
end
-
end
-
1
private :add_any_wells_from_template_or_partial
-
-
1
def add_empty_well(wells)
-
wells << CherrypickTask::EMPTY_WELL
-
end
-
1
private :add_empty_well
-
end
-
-
1
def pick_new_plate(requests, template, robot, batch, plate_purpose)
-
target_type = PickTarget.for(plate_purpose)
-
perform_pick(requests, robot, batch) do |batch|
-
target_type.new(batch, template, plate_purpose.try(:asset_shape))
-
end
-
end
-
-
1
def pick_onto_partial_plate(requests, template, robot, batch, partial_plate)
-
purpose = partial_plate.plate_purpose
-
target_type = PickTarget.for(purpose)
-
-
perform_pick(requests, robot, batch) do |batch|
-
target_type.new(batch, template, purpose.try(:asset_shape), partial_plate).tap do
-
partial_plate = nil # Ensure that subsequent calls have no partial plate
-
end
-
end
-
end
-
-
1
def perform_pick(requests, robot, batch, &block)
-
max_plates = robot.max_beds
-
raise StandardError, 'The chosen robot has no beds!' if max_plates.zero?
-
-
batch = BatchWrapper.new(self, batch)
-
plates, current_plate = [], yield(batch)
-
source_plates, current_sources = Set.new, Set.new
-
plates_hash = build_plate_wells_from_requests(requests)
-
-
push_completed_plate = lambda do
-
plates << current_plate.completed_view
-
current_sources.clear
-
current_plate = yield(batch)
-
end
-
-
plates_hash.each do |request_id, plate_barcode, well_location|
-
# Doing this here ensures that the plate_barcode being processed will be the first
-
# well on the new plate.
-
unless current_sources.include?(plate_barcode)
-
push_completed_plate.call if not current_sources.empty? and (current_sources.size % max_plates).zero? and not current_plate.empty?
-
source_plates << plate_barcode
-
current_sources << plate_barcode
-
end
-
-
# Add this well to the pick and if the plate is filled up by that push it to the list.
-
current_plate.push(request_id, plate_barcode, well_location)
-
push_completed_plate.call if current_plate.full?
-
end
-
-
# Ensure that a non-empty plate is stored and that the control plate is added if it has been used
-
push_completed_plate.call unless current_plate.empty?
-
source_plates << ControlPlate.first.barcode if batch.control_added?
-
-
[plates, source_plates]
-
end
-
1
private :perform_pick
-
-
1
def partial
-
"cherrypick_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_cherrypick_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_cherrypick_task(self, params)
-
rescue Cherrypick::Error => exception
-
workflow.send(:flash)[:error] = exception.message
-
return false
-
end
-
-
1
def build_plate_wells_from_requests(requests)
-
Request.select(['requests.id AS id', 'plates.barcode AS barcode', 'maps.description AS description']).
-
joins([
-
'INNER JOIN assets wells ON requests.asset_id=wells.id',
-
'INNER JOIN container_associations ON container_associations.content_id=wells.id',
-
'INNER JOIN assets plates ON plates.id=container_associations.container_id',
-
'INNER JOIN maps ON wells.map_id=maps.id'
-
]).
-
order('plates.barcode ASC, maps.column_order ASC').
-
where(:requests => { :id => requests }).
-
all.map do |request|
-
[request.id, request.barcode, request.description]
-
end
-
end
-
1
private :build_plate_wells_from_requests
-
-
1
def generate_control_request(well)
-
# TODO: create a genotyping request for the control request
-
#Request.create(:state => "pending", :sample => well.sample, :asset => well, :target_asset => Well.create(:sample => well.sample, :name => well.sample.name))
-
workflow.pipeline.control_request_type.create_control!(
-
:asset => well,
-
:target_asset => Well.create!(:aliquots => well.aliquots.map(&:dup))
-
)
-
end
-
1
private :generate_control_request
-
-
1
def get_well_from_control_param(control_param)
-
control_param.scan(/([\d]+)/)
-
well_id = $1.to_i
-
Well.find_by_id(well_id)
-
end
-
1
private :get_well_from_control_param
-
-
1
def create_control_request_from_well(control_param)
-
return nil unless control_param.match(/control/)
-
well = get_well_from_control_param(control_param)
-
return nil if well.nil?
-
generate_control_request(well)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class CherrypickingPipeline < GenotypingPipeline
-
-
1
def custom_inbox_actions
-
[:holder_not_control]
-
end
-
-
1
def inbox_eager_loading
-
:loaded_for_grouped_inbox_display
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class Comment < ActiveRecord::Base
-
# include Uuid::Uuidable
-
1
belongs_to :commentable, :polymorphic => true
-
1
has_many :comments, :as => :commentable
-
1
belongs_to :user
-
-
1
scope :for_plate, ->(plate) {
-
-
submissions = plate.all_submission_ids
-
-
# Warning: The code below is utterly horrible, and has already be the source of several bugs
-
# It needs to be completely re-thought. The main difficulties are:
-
# Comments can be both on the requests or on the plate itself
-
# Rails handles counts on group statements strangely (See the Comments proxy on plate)
-
-
if submissions.present?
-
rids = Request.find(:all,:select=>'id',:conditions=>{:submission_id=>submissions}).map(&:id)
-
where([
-
'(commentable_type= "Request" AND commentable_id IN (?)) OR (commentable_type = "Asset" and commentable_id = ?)',
-
rids,plate.id
-
]).group('CONCAT(comments.description, IFNULL(comments.title,""), comments.user_id)')
-
# The above group by is grim, and is due to the way rails generates the key to help count
-
# grouped statements. Essentially is adds AS on the end to create a new column. If we don't
-
# concat, then if just uses the last element. The IFNULL is necessary as it seems that
-
# if any element of a CONCAT statement is NULL, MySQL just returns NULL
-
else
-
where(['comments.commentable_type = "Asset" and commentable_id = ?', plate.id])
-
end
-
-
}
-
-
1
scope :include_uuid, -> { where('TRUE') }
-
-
1
def self.counts_for(commentables)
-
return 0 if commentables.empty?
-
type = commentables.first.class.base_class.name
-
where(:commentable_type=>type,:commentable_id=>commentables).group(:commentable_id).count
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
class ContainerAssociation < ActiveRecord::Base
-
#We don't define the class, so will get an error if being used directly
-
# in fact , the class need to be definend otherwise, eager loading through doesn't work
-
belongs_to :container , :class_name => "Asset"
-
belongs_to :content , :class_name => "Asset"
-
-
# NOTE: This was originally on the content asset but this causes massive performance issues.
-
# It causes the plate and it's metadata to be loaded for each well, which would be cached if
-
# it were not for inserts/updates being performed. I'm disabling this as it should be caught
-
# in tests and we've not seen it in production.
-
#
-
# # We check if the parent has already been saved. if not the saving will not work.
-
# before_save do |content|
-
# container = content.container
-
# raise RuntimeError, "Container should be saved before saving #{self.inspect}" if container && container.new_record?
-
# end
-
-
module Extension
-
def contains(content_name, options = {}, &block)
-
class_name = content_name ? content_name.to_s.classify : Asset.name
-
has_many :container_associations, :foreign_key => :container_id
-
has_many :contents, options.merge(:class_name => class_name, :through => :container_associations)
-
has_many(content_name, options.merge(:class_name => class_name, :through => :container_associations, :source => :content)) do
-
# Provide bulk importing abilities. Inside a transaction we can guarantee that the information in the DB is
-
# consistent from our perspective. In other words, we can bulk insert the records and then reload them, limited
-
# by their count, to obtain the IDs.
-
#
-
# WARNING: We have to be extremely careful about how to select the appropriate data from the bulk insert as
-
# the DB can choose to ignore the ordering. To get round this we perform one query in the correct order and
-
# then limit it, rather than doing these two steps together.
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def import(records)
-
ActiveRecord::Base.transaction do
-
records.map(&:save!)
-
attach(records)
-
post_import(records.map { |r| [proxy_association.owner.id, r['id']] })
-
end
-
end
-
}, __FILE__, line)
-
-
1
def attach(records)
-
ActiveRecord::Base.transaction do
-
records.each { |r| ContainerAssociation.create!(:container_id => proxy_association.owner.id, :content_id => r.id) }
-
end
-
end
-
-
# Sometimes we need to do things after importing the contained records. This is the callback that should be
-
# overridden by the block passed.
-
1
def post_import(_)
-
# Does nothing by default
-
end
-
-
1
def connect(content)
-
ContainerAssociation.create!(:container => proxy_association.owner, :content => content)
-
post_connect(content)
-
end
-
1
private :connect
-
-
1
class_eval(&block) if block_given?
-
end
-
-
1
self.class_eval do
-
1
def maps
-
Map.where_plate_size(size).where_plate_shape(asset_shape)
-
end
-
end
-
-
1
scope :"include_#{content_name}", -> { includes(:contents) } do
-
1
def to_include
-
[:contents]
-
end
-
-
1
def with(subinclude)
-
scoped(:include => { :contents => subinclude })
-
end
-
end
-
end
-
-
def contained_by(container_name, &block)
-
2
class_name = container_name.to_s.singularize.capitalize
-
2
has_one :container_association, :foreign_key => :content_id
-
2
has_one :container, :class_name => class_name, :through => :container_association
-
2
has_one(container_name, :class_name => class_name, :through => :container_association, :source => :container, &block)
-
-
#delegate :location, :to => :container
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Control < ActiveRecord::Base
-
1
belongs_to :pipeline
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class ControlPlate < Plate
-
1
self.prefix = "DN"
-
-
1
ILLUMINA_CONTROL_WELL_LOCATIONS = [ 'A1', 'C1', 'E1' ]
-
-
1
def illumina_wells
-
self.wells.all(:conditions => [ 'maps.description IN (?) AND maps.asset_size=?', ILLUMINA_CONTROL_WELL_LOCATIONS, 96 ], :include => :map)
-
end
-
-
1
def affy_wells
-
self.wells.select{|well| well.map_id == 73|| well.map_id == 85}
-
end
-
1
deprecate(:affy_wells => 'assumed this was not used, needs map_id fixes')
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014,2015 Genome Research Ltd.
-
1
class ControlRequest < CustomerRequest
-
1
include Request::HasNoTargetAsset
-
1
include Api::Messages::FlowcellIO::ControlLaneExtensions
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
class CreateAssetRequest < SystemRequest
-
1
def initialize_aliquots
-
# set study on aliquot
-
asset.try(:aliquots).try(:each) do |aliquot|
-
return if aliquot.study_id || aliquot.project_id
-
aliquot.update_attributes!(:study_id => self.initial_study_id, :project_id => self.initial_project_id)
-
end
-
end
-
1
private :initialize_aliquots
-
1
before_save :initialize_aliquots
-
-
# CreateAssetRequests should only be generated for sample tubes, wells on
-
# stock plates or library tubes
-
1
validate :on_valid_asset?
-
1
def on_valid_asset?
-
return true if asset.can_be_created?
-
errors.add :asset, "should be either a sample tube, a well on a stock plate or a library tube from a manifest."
-
false
-
end
-
1
private :on_valid_asset?
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class CustomText < ActiveRecord::Base
-
1
after_save :clear_text_cache!
-
-
# If the value of this CustomText instance was saved in cache
-
# e.g. the appication wide information box, delete it.
-
1
def clear_text_cache!
-
Rails.cache.delete(name)
-
end
-
-
1
def name
-
"#{identifier}-#{differential}"
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# A class for requests that have some business meaning outside of Sequencescape
-
1
class CustomerRequest < Request
-
-
1
def update_responsibilities!
-
return if qc_metrics.stock_metric.empty?
-
self.customer_accepts_responsibility! if qc_metrics.stock_metric.all?(&:poor_quality_proceed)
-
end
-
-
1
def customer_accepts_responsibility!
-
self.request_metadata.update_attributes!(:customer_accepts_responsibility=>true)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
module DataRelease
-
# TODO[xxx]: All of this probably falls into the Study::Metadata class
-
-
1
def valid_data_release_properties?
-
return true unless self.enforce_data_release
-
return false if self.study_metadata.data_release_study_type.try(:is_not_specified?)
-
return false if self.study_metadata.data_release_strategy.try(:blank?)
-
return false if self.study_metadata.data_release_timing.try(:blank?)
-
true
-
end
-
-
1
def ena_accession_required?
-
return false unless self.enforce_accessioning
-
return true unless valid_data_release_properties?
-
return false if self.study_metadata.data_release_study_type.try(:studies_excluded_for_release?)
-
# TODO[xxx]: was this removed?
-
return false if [ 'never', 'delayed' ].include?(self.study_metadata.data_release_timing)
-
true
-
end
-
-
1
def all_samples_have_accession_numbers?
-
samples.all?(&:accession_number?)
-
end
-
-
1
def for_array_express?
-
(st=self.study_metadata.data_release_study_type) && st.for_array_express
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class DataReleaseStudyType < ActiveRecord::Base
-
1
extend Attributable::Association::Target
-
-
1
has_many :study
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :message => "of data release study type already present in database"
-
-
1
scope :assay_types, -> { where( :is_assay_type => true ) }
-
1
scope :non_assay_types, -> { where( :is_assay_type => false ) }
-
-
1
DATA_RELEASE_TYPES_SAMPLES = ['genotyping or cytogenetics' ]
-
1
DATA_RELEASE_TYPES_STUDIES = []
-
-
1
def is_not_specified?
-
false
-
end
-
-
1
def studies_excluded_for_release?
-
DATA_RELEASE_TYPES_STUDIES.include?(self.name)
-
end
-
-
1
def samples_excluded_for_release?
-
DATA_RELEASE_TYPES_SAMPLES.include?(self.name)
-
end
-
-
1
def self.default
-
first(:conditions => { :is_default => true })
-
end
-
-
1
module Associations
-
1
def self.included(base)
-
1
base.validates_presence_of :data_release_study_type_id
-
1
base.belongs_to :data_release_study_type
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
class DbFile < ActiveRecord::Base
-
# This is the model for database storage
-
-
# Polymorphic so that many models can use this class to store binary data
-
belongs_to :owner, :polymorphic => true
-
# Note: We are constrained by the database to split files into 200kbyte partitions
-
-
# This module will set up all required associations and allow mounting "polymorphic uploaders"
-
module Uploader
-
def self.extended(base)
-
base.has_many :db_files, :as => :owner, :dependent => :destroy
-
end
-
-
# Mount an uploader on the specified 'data' column
-
# - you can use the serialisation option for saving the filename in another column - see Carrierwave
-
def has_uploaded(data, options)
-
serialization_column = options.fetch(:serialization_column, "#{data}")
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
mount_uploader data, PolymorphicUploader, :mount_on => serialization_column
-
}, __FILE__, line)
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
# Delegate validation is all about enabling one class to validate the information within an instance of
-
# another class. The case driving this is the ability for a Submission to validate that the request options
-
# provided by the user are valid for the RequestType instances that the submission is going to use. In that
-
# case the RequestType#delegate_validator returns a class that can then be used to validate the request options.
-
# Because RequestType isn't subclassed it actually delegates to the Request class that it'll instantiate, so
-
# you can find examples of the delegator stuff in SequencingRequest and LibraryCreationRequest
-
module DelegateValidation
-
def delegate_validation(*args)
-
options = args.extract_options!
-
delegation_target = options.delete(:to) or raise StandardError, "Cannot delegate validation without :to!"
-
attribute_tag = options[:as]
-
args.push(options)
-
-
validates_each(*args) do |record, attr, value|
-
validator = record.send(:"#{delegation_target}_delegate_validator").new(value)
-
validator.valid?.tap do
-
validator.errors.messages.each do |attrib,message|
-
record.errors.add("#{attribute_tag}.#{attrib}",message.join('; '))
-
end
-
end
-
end
-
end
-
-
class Validator
-
include Validateable
-
-
class DelegateError < ActiveModel::Errors
-
def initialize(base,target)
-
@base = base
-
@messages = target.errors.messages
-
end
-
end
-
-
attr_reader :target
-
protected :target
-
delegate :include_unset_values?, :to => :target
-
-
-
def self.name
-
2
'Nothing'
-
end
-
-
def initialize(target)
-
@target = target
-
end
-
-
def self.delegate_attribute(*args)
-
options = args.extract_options!
-
type_cast = ".#{ options[:type_cast] }" if options.key?(:type_cast) && options[:type_cast].present?
-
default = " || #{options[:default].inspect}" if options.key?(:default)
-
-
args.each do |attribute|
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{attribute}_before_type_cast
-
#{options[:to]}.#{attribute} #{default}
-
end
-
-
1
def #{attribute}
-
#{attribute}_before_type_cast#{type_cast}
-
end
-
-
1
def #{attribute}_needs_checking?
-
#{attribute}_before_type_cast.present? or include_unset_values?
-
end
-
}, __FILE__, line)
-
end
-
end
-
end
-
-
# A simple validator that is always assumed to be valid.
-
class AlwaysValidValidator < Validator
-
def valid?
-
true
-
end
-
end
-
-
# A composite validator that will perform multiple validations across several validator classes.
-
class CompositeValidator
-
include ActiveModel::Validations
-
class_attribute :validator_classes, :instance_writer => false
-
-
def self.CompositeValidator(*validator_classes)
-
Class.new(CompositeValidator).tap do |sub_class|
-
sub_class.validator_classes = validator_classes
-
end
-
end
-
-
def initialize(target)
-
@target = target
-
@validators = self.class.validator_classes.map { |c| c.new(target) }
-
end
-
-
def valid?
-
# We have to run over all validators to get all error messages, then we can check they're all valid
-
return true if @validators.map(&:valid?).all? { |v| v == true }
-
@validators.each do |validator|
-
errors.messages.merge!(validator.errors.messages)
-
end
-
false
-
end
-
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Descriptor < ActiveRecord::Base
-
1
belongs_to :task
-
1
serialize :selection
-
-
1
def is_required?
-
self.required
-
end
-
-
1
def matches?(search)
-
search.descriptors.each do |descriptor|
-
if descriptor.name == self.name && descriptor.value == self.value
-
return true
-
end
-
end
-
false
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class DilutionPlate < Plate
-
-
# We have to put the asset_links.direct condition on here, rather than go through :links_as_parent as it seems that
-
# rails doesn't cope with conditions on has_many_through relationships where the relationship itself also have conditions
-
1
scope :with_pico_children, -> {
-
joins(:pico_descendants).
-
select('DISTINCT `assets`.*').
-
where(:asset_links=>{:direct =>true})
-
}
-
-
1
has_many :pico_descendants,
-
:through => :links_as_ancestor,
-
:conditions => { :sti_type=>[PicoAssayPlate,PicoAssayAPlate,PicoAssayBPlate].map(&:name) },
-
:source => :descendant
-
-
1
def pico_children
-
pico_descendants.find(:all,:conditions=>['asset_links.direct = ?',true])
-
end
-
-
1
def to_pico_hash
-
{:pico_dilution => {
-
:child_barcodes => pico_children.map{ |plate| plate.barcode_dilution_factor_created_at_hash }
-
}.merge(barcode_dilution_factor_created_at_hash),
-
:study_name => study_name
-
}
-
end
-
-
1
def study_name
-
study.try(:name) || ""
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class DilutionPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class DnaQcPipeline < GenotypingPipeline
-
1
include Pipeline::InboxGroupedBySubmission
-
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
1
def post_finish_batch(batch, user)
-
# Nothing, we don't want all the requests to be completed
-
end
-
-
1
def post_release_batch(batch, user)
-
batch.release_pending_requests()
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
class DnaQcTask < Task
-
-
1
class QcData < Task::RenderElement
-
1
attr_reader :gel_value, :pico_value, :sequenom_value, :initial_concentration, :gender_value, :gender_markers_value, :genotyping_done, :sample_empty, :volume
-
1
alias_attribute :well, :asset
-
-
1
def initialize(request)
-
super(request)
-
-
primary_sample = well.primary_aliquot.try(:sample)
-
if primary_sample.present?
-
@genotyping_done = primary_sample.get_external_value('genotyping_done')
-
@genotyping_done = primary_sample.genotyping_done
-
@sample_empty = primary_sample.empty_supplier_sample_name
-
end
-
-
@pico_value = well.get_pico_pass
-
@gel_value = well.get_gel_pass
-
@sequenom_count = well.get_sequenom_count
-
@initial_concentration = well.get_concentration
-
@gender_value = primary_sample.try(:sample_metadata).try(:gender)
-
@gender_markers_value = well.get_gender_markers
-
@sequenom_value = "#{@sequenom_count}/30 #{@gender_markers_value}"
-
@volume = well.well_attribute.measured_volume
-
end
-
-
1
def qc_status
-
status = [gel_status, pico_status, sequenom_status, concentration_status, gender_status, sample_name_empty]
-
-
return "fail" if genotyping_done_status == "fail"
-
return case
-
when status.map { |s| s == "pass" or s == "*" }.all? then "pass"
-
when status.map { |s| s == "fail" or s == "*" or s.nil?}.all? then "fail"
-
when status.map { |s| s == "fail"}.select{ |b| b == true }.size >=3 then "fail"
-
else ""
-
end
-
end
-
-
1
def sample_name_empty
-
case
-
when sample_empty then "fail"
-
else "pass"
-
end
-
end
-
-
1
def pico_status
-
case
-
when pico_value == "Pass" || pico_value == "passed" then "pass"
-
when pico_value == "ungraded" || pico_value == "repeat" then "*"
-
when pico_value == "failed" then "fail"
-
when ["Too Low To Normalise"].include?(pico_value) then "fail"
-
else ""
-
end
-
end
-
-
1
def gel_status
-
case
-
when ["Fail", "Weak", "Band Not Visible", "Degraded"].include?(gel_value) then "fail"
-
when gel_value == "OK" then "*"
-
when gel_value.blank? then "fail"
-
else ""
-
end
-
end
-
1
def sequenom_status
-
return "*" unless @sequenom_count
-
count = @sequenom_count.to_i
-
case
-
when count < 19 then "fail"
-
when count >19 then "pass"
-
end
-
end
-
1
def concentration_status
-
case
-
when initial_concentration.nil? then "fail"
-
when initial_concentration.to_i < 35 then "fail"
-
when initial_concentration.to_i > 50 then "pass"
-
end
-
end
-
-
1
def gender_status
-
return "*" if @gender_value == "Unknown" || @gender_value.nil?
-
if @gender_value.match(/^f/i)
-
@gender_value = 'F'
-
elsif @gender_value.match(/^m/i)
-
@gender_value = 'M'
-
end
-
-
case
-
when @gender_markers_value.blank? then "*"
-
when @gender_markers_value.last != @gender_value then "fail"
-
when @gender_value.map { |g| g == @gender_value }.all? then "pass"
-
end
-
end
-
-
1
def genotyping_done_status
-
@genotyping_done && @genotyping_done != "0" ? "fail" : "pass"
-
end
-
end # class QcData
-
-
-
-
1
def create_render_element(request)
-
request.asset && QcData.new(request)
-
end
-
-
1
def partial
-
"dna_qc_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_dna_qc_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_dna_qc_task(self, params)
-
end
-
-
1
def pass_request(request, batch, state)
-
return if state.blank?
-
-
case state
-
when "pass"
-
if request.pass
-
logger.debug "SENDING PASS FOR REQUEST #{request.id}, BATCH #{batch.id}"
-
EventSender.send_pass_event(request.id, "", "Passed DNQ QC", batch.id)
-
-
#activate the next requets
-
request.next_requests(batch.pipeline).each do |next_request|
-
if next_request.blocked? and next_request.unblock
-
next_request.save
-
end
-
end
-
end
-
when "fail"
-
if request.fail
-
logger.debug "SENDING FAIL FOR REQUEST #{request.id}, BATCH #{batch.id}"
-
EventSender.send_fail_event(request.id, "", "failed DNQ QC", batch.id)
-
-
#cancel next request
-
request.next_requests(batch.pipeline).each do |next_request|
-
next_request.cancel_before_started!
-
end
-
end
-
end
-
-
event = LabEvent.new
-
event.description = name
-
event.eventful = request
-
event.add_new_descriptor("Passed", request.state == "passed")
-
-
request.save!
-
event.save!
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
-
require 'carrierwave'
-
-
class Document < ActiveRecord::Base
-
extend DbFile::Uploader
-
-
module Associations
-
# Adds accessors for named fields and attaches documents to them
-
-
def has_uploaded_document(field, options={})
-
# Options
-
# differentiator - this is a string used to separate multiple documents related to your model
-
# for example, you can have both a "generated" and an "uploaded" document in one Sample Manifest
-
differentiator = options.fetch(:differentiator, "#{field}")
-
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
has_one(:#{field}_document, :class_name => "Document", :as => :documentable, :conditions => {:documentable_extended => differentiator}, :dependent => :destroy
-
)
-
-
1
def #{field}
-
self.#{field}_document
-
end
-
-
1
def #{field}=(file)
-
create_#{field}_document(:uploaded_data => file, :documentable_extended => '#{differentiator}') unless file.blank?
-
end
-
}, __FILE__, line)
-
end
-
-
-
end
-
-
# Polymorphic relationship
-
belongs_to :documentable, :polymorphic => true
-
-
# CarrierWave uploader - gets the uploaded_data file, but saves the identifier to the "filename" column
-
has_uploaded :uploaded_data, {:serialization_column => "filename"}
-
-
# Method provided for backwards compatibility
-
def current_data
-
uploaded_data.read
-
end
-
-
# Handle some of the metadata with this callback
-
before_save :update_document_attributes
-
-
# Save Size/content_type Metadata
-
def update_document_attributes
-
if uploaded_data.present?
-
self.content_type = uploaded_data.file.content_type
-
self.size = uploaded_data.file.size
-
end
-
end
-
private :update_document_attributes
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013 Genome Research Ltd.
-
1
class EgaAccessionService < AccessionService
-
1
def accession_from_ebi(submission_filename, submission_file_handle, type_filename, type_file_handle, type)
-
generate_accession_from_ebi(submission_filename, submission_file_handle, type_filename, type_file_handle, type, configatron.ega_accession_login)
-
end
-
-
1
def provider
-
:EGA
-
end
-
-
1
def accession_login
-
configatron.ega_accession_login or raise RuntimeError, "Can't find EGA accession login in configuration file"
-
end
-
-
1
def sample_visibility(sample)
-
Protect
-
end
-
-
1
def study_visibility(study)
-
Protect
-
end
-
-
1
def broker
-
"EGA"
-
end
-
-
1
def submit_dac_for_user(study, user)
-
submit(user, Accessionable::Dac.new(study))
-
end
-
-
1
def submit_policy_for_user(study, user)
-
policy = Accessionable::Policy.new(study)
-
submit(user, policy)
-
end
-
-
1
def private?
-
true
-
end
-
-
#def submit(user, *accessionables)
-
#accessionables.each(&:protect)
-
-
#super(user, *accessionables)
-
#end
-
end
-
1
class Equipment < ActiveRecord::Base
-
-
1
validates_presence_of :name, :equipment_type
-
1
before_validation :set_defaults
-
1
after_create :update_barcode
-
-
1
def set_defaults
-
self.prefix||='XX'
-
end
-
-
1
def update_barcode
-
self.ean13_barcode ||= Barcode.calculate_barcode(prefix, id)
-
save!
-
end
-
-
1
def barcode_number
-
Barcode.number_to_human(self.ean13_barcode)
-
end
-
-
1
def suffix
-
Barcode.calculate_checksum(prefix, barcode_number)
-
end
-
-
1
def printables
-
[PrintBarcode::Label.new({
-
:number => barcode_number,
-
:study => name,
-
:suffix => suffix,
-
:prefix => prefix,
-
:type => "custom-labels",
-
:label_description => name })]
-
end
-
-
1
def print(barcode_printer)
-
begin
-
unless printables.empty?
-
barcode_printer.print_labels(printables)
-
end
-
rescue
-
return false
-
end
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
class EraAccessionService < AccessionService
-
-
1
def provider
-
:ERA
-
end
-
-
1
def accession_from_ebi(submission_filename, submission_file_handle, type_filename, type_file_handle, type)
-
generate_accession_from_ebi(submission_filename, submission_file_handle, type_filename, type_file_handle, type, configatron.era_accession_login)
-
end
-
-
1
def accession_login
-
configatron.era_accession_login or raise RuntimeError, "Can't find ERA accession login in configuration file"
-
end
-
# Most uses of this feature have been human error, so its better to hold off on releasing data than accidentally releasing data
-
1
def sample_visibility(sample)
-
#sample_hold = sample.sample_sra_hold
-
#sample_hold.blank? ? 'hold' : sample_hold
-
Hold
-
end
-
-
1
def study_visibility(study)
-
#study_hold = study.study_sra_hold
-
#study_hold.blank? ? 'hold' : study_hold
-
Hold
-
end
-
-
1
def policy_visibility(study)
-
Hold
-
end
-
-
1
def dac_visibility(study)
-
Hold
-
end
-
-
1
def broker
-
nil
-
end
-
-
1
def submit_policy_for_user(user, study)
-
raise NumberNotGenerated, "no need to submit Policy to ERA"
-
end
-
-
1
def submit_dac_for_user(user, study)
-
raise NumberNotGenerated, "no need to submit DAC to ERA"
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2015 Genome Research Ltd.
-
1
class Event < ActiveRecord::Base
-
1
include Api::EventIO::Extensions
-
1
include Uuid::Uuidable
-
-
1
self.per_page = 500
-
1
belongs_to :eventful, :polymorphic => true
-
1
after_create :rescuing_update_request, :unless => :need_to_know_exceptions?
-
1
after_create :update_request, :if => :need_to_know_exceptions?
-
-
1
scope :family_pass_and_fail, -> { where(:family => ["pass", "fail"]).order('id DESC') }
-
1
scope :npg_events, ->(*args) { {:conditions => ["created_by='npg' and eventful_id = ? ", args[0]] }}
-
-
1
attr_writer :need_to_know_exceptions
-
1
def need_to_know_exceptions?
-
@need_to_know_exceptions
-
end
-
-
1
def request?
-
self.eventful_type == "Request" ? true : false
-
end
-
-
1
private
-
-
1
include Event::AssetDescriptorUpdateEvent
-
1
include Event::RequestDescriptorUpdateEvent
-
-
1
def rescuing_update_request
-
update_request
-
end
-
-
1
def update_request
-
if self.request?
-
request = self.eventful
-
unless request.nil? or request.failed? or request.cancelled?
-
if self.family == "fail"
-
request.fail!
-
elsif self.family == "pass"# && !request.project.nil?
-
request.pass!
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Event::AssetDescriptorUpdateEvent
-
1
def self.included(base)
-
1
base.after_create(:update_descriptors_for_asset, :if => ->(event) { event.eventful.is_a?(Asset) and not event.descriptor_key.blank? })
-
end
-
-
1
def update_descriptors_for_asset
-
self.eventful.add_descriptor(Descriptor.new(:name => self.descriptor_key, :value => self.content))
-
self.eventful.save!
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Event::AssetSetQcStateEvent < Event
-
1
class << self
-
1
def self.constructor_for_event_type(type)
-
2
define_method(:"create_#{ type }!") do |asset, reason|
-
self.create!(
-
:eventful => asset,
-
:family => "update",
-
:content => reason,
-
:message => reason
-
)
-
end
-
end
-
-
1
constructor_for_event_type('passed')
-
1
constructor_for_event_type('failed')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Event::PlateCreationEvent < Event
-
1
def self.create_for_asset!(asset, plate_purpose, child_plate, user)
-
self.create!(
-
:eventful => asset,
-
:message => "Created child #{plate_purpose.name} plate",
-
:content => Date.today.to_s,
-
:family => "create_#{plate_purpose.class.name.underscore}",
-
:created_by => user ? user.login : nil
-
)
-
self.create!(
-
:eventful => child_plate,
-
:message => "Created #{plate_purpose.name} plate",
-
:content => Date.today.to_s,
-
:family => "create_#{plate_purpose.class.name.underscore}",
-
:created_by => user ? user.login : nil
-
)
-
end
-
-
1
def self.create_for_asset_with_date!(asset, plate_purpose, parent_plate, date)
-
self.create!(
-
:eventful => asset,
-
:message => "Created #{plate_purpose.name} from #{parent_plate.id}",
-
:content => date.to_s,
-
:family => "create_#{plate_purpose.class.name.underscore}"
-
)
-
end
-
-
1
def self.create_sequenom_stamp_for_asset!(asset, user)
-
self.create!(
-
:eventful => asset,
-
:message => "Stock plate appears on a plate for Sequenom",
-
:content => Date.today.to_s,
-
:family => "create_for_sequenom",
-
:created_by => user ? user.login : nil
-
)
-
end
-
1
def self.create_sequenom_plate_for_asset!(asset, user)
-
self.create!(
-
:eventful => asset,
-
:message => "Created Sequenom plate",
-
:content => Date.today.to_s,
-
:family => "create_sequenom_plate",
-
:created_by => user ? user.login : nil
-
)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Event::PlateEvents
-
1
def gel_qc_date
-
event_date('gel_analysed')
-
end
-
-
1
def pico_date
-
event_date('pico_analysed')
-
end
-
-
1
def qc_started_date
-
event_date('create_dilution_plate_purpose')
-
end
-
-
1
def sequenom_stamp_date
-
event_date('create_for_sequenom')
-
end
-
-
1
def event_date(key)
-
event = self.events.find_by_family(key)
-
return event.content if event
-
-
nil
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Event::RequestDescriptorUpdateEvent
-
1
def self.included(base)
-
1
base.after_create(:update_metadata_for_request, :if => ->(event) { event.eventful.is_a?(Request) and not event.descriptor_key.blank? })
-
end
-
-
1
def pass_or_fail_event?
-
[ 'fail', 'pass' ].include?(self.family)
-
end
-
-
1
def library_creation_descriptor?
-
[ 'library_creation_complete', 'multiplexed_library_creation' ].include?(self.descriptor_key)
-
end
-
-
1
def set_request_metadata
-
eventful.request_metadata[ self.descriptor_key ] = self.content
-
eventful.request_metadata.save!
-
end
-
-
1
def update_metadata_for_request
-
request = self.eventful
-
self.set_request_metadata unless self.pass_or_fail_event?
-
-
if request.failed? or request.cancelled?
-
self.set_request_metadata
-
return
-
end
-
-
return if self.pass_or_fail_event?
-
if self.library_creation_descriptor?
-
request.pass!
-
else
-
request.start!
-
end
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Event::SampleLogisticsQcEvent < Event
-
1
def self.create_gel_qc_for_asset!(asset, result, user)
-
if asset.is_a?(Well)
-
gel_qc_message(asset, "Gel Analysed for well #{asset.id} with #{result}", "gel_analysed", user)
-
elsif asset.is_a?(Plate)
-
gel_qc_message(asset, "Gel Analysed", "gel_analysed", user)
-
end
-
end
-
-
1
def self.gel_qc_message(asset, message, family, user)
-
self.create!(
-
:eventful => asset,
-
:message => message,
-
:content => Date.today.to_s,
-
:family => family,
-
:created_by => user ? user.login : nil
-
)
-
end
-
-
1
def self.pico_qc_message(asset, message, family)
-
self.create!(
-
:eventful => asset,
-
:message => message,
-
:content => Date.today.to_s,
-
:family => family
-
)
-
end
-
-
1
def self.create_pico_result_for_asset!(asset, result)
-
if asset.is_a?(Well)
-
pico_qc_message(asset, "Pico result for well #{asset.id} with #{result}", "pico_analysed")
-
elsif asset.is_a?(Plate)
-
pico_qc_message(asset, "Pico result for plate #{asset.barcode} with #{result}", "pico_analysed")
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Event::SampleManifestEvent< Event
-
1
def self.created_sample!(sample, user)
-
self.create!(
-
:eventful => sample,
-
:message => 'Created by Sample Manifest',
-
:content => Date.today.to_s,
-
:family => 'created_sample_using_sample_manifest',
-
:created_by => user ? user.login : nil
-
)
-
end
-
-
1
def self.updated_sample!(sample, user)
-
self.create!(
-
:eventful => sample,
-
:message => 'Updated by Sample Manifest',
-
:content => Date.today.to_s,
-
:family => 'updated_sample_using_sample_manifest',
-
:created_by => user ? user.login : nil
-
)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Event::ScannedIntoLabEvent < Event
-
1
after_create :set_qc_state_pending, :unless => :test?
-
1
alias_method :asset, :eventful
-
-
1
def self.create_for_asset!(asset, location)
-
self.create!(
-
:eventful => asset,
-
:message => "Scanned into #{location.name}",
-
:content => Date.today.to_s,
-
:family => "scanned_into_lab"
-
)
-
-
end
-
-
1
def set_qc_state_pending
-
self.asset.qc_pending
-
end
-
-
1
def test?
-
return (self.asset.qc_state == "passed" || self.asset.qc_state == "failed")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
class Event::SequenomLoading < Event
-
1
def self.created_update_gender_makers!(asset, resource)
-
self.create!(
-
:eventful => asset,
-
:message => "Updated gender results from #{resource}",
-
:content => resource,
-
:family => "update_gender_markers"
-
)
-
end
-
-
1
def self.created_update_sequenom_count!(asset, resource)
-
self.create!(
-
:eventful => asset,
-
:message => "Updated sequenom results from #{resource}",
-
:content => resource,
-
:family => "update_sequenom_count"
-
)
-
end
-
-
1
def self.updated_fluidigm_plate!(asset, resource)
-
self.create!(
-
:eventful => asset,
-
:message => "Updated fluidigm plate from #{resource}",
-
:content => resource,
-
:family => "update_fluidigm_plate"
-
)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class EventSender
-
-
# format message expected output
-
# <?xml version='1.0'?>
-
# <event>
-
# <message>win</message>
-
# <eventful_id>123</eventful_id>
-
# <eventful_type>Request</eventful_type>
-
# <descriptor_key>failure</descriptor_key>
-
# <content>fail</content>
-
# <family>fail</family>
-
# <identifier>123</identifier>
-
# </event>
-
1
def self.format_message(hash)
-
doc = hash.to_xml(:root => "event", :skip_types => true)
-
doc.to_s.gsub!('-', '_').gsub!('UTF_8', 'UTF-8')
-
end
-
-
1
def self.send_fail_event(request_id, reason, comment, batch_id, user=nil, options = nil)
-
hash = { :eventful_id => request_id, :eventful_type => 'Request', :family => "fail", :content => reason, :message => comment, :identifier => batch_id, :key => "failure", :created_by => user }
-
self.publishing_to_queue(hash.merge(options || {}))
-
end
-
-
1
def self.send_cancel_event(request_id, reason, comment, options = nil)
-
hash = { :eventful_id => request_id, :eventful_type => 'Request', :family => "cancel", :content => reason, :message => comment, :identifier => request_id, :key => "cancel" }
-
self.publishing_to_queue(hash.merge(options || {}))
-
end
-
-
1
def self.send_pass_event(request_id, reason, comment, batch_id, user=nil, options = nil)
-
hash = { :eventful_id => request_id, :eventful_type => 'Request', :family => "pass", :content => reason, :message => comment, :identifier => batch_id, :key => "pass", :created_by => user }
-
self.publishing_to_queue(hash.merge(options || {}))
-
end
-
-
1
def self.send_request_update(request_id, family, message, options = nil)
-
hash = { :eventful_id => request_id, :eventful_type => 'Request', :family => family, :message => message }
-
self.publishing_to_queue(hash.merge(options || {}))
-
end
-
-
1
def self.send_pick_event(well_id, purpose_name, message, options = nil)
-
hash = { :eventful_id => well_id, :eventful_type => 'Asset', :family => PlatesHelper::event_family_for_pick(purpose_name), :message => message }
-
self.publishing_to_queue(hash.merge(options || {}))
-
end
-
-
1
private
-
-
1
def self.publishing_to_queue(hash = {})
-
hash.delete(:key)
-
Event.create(hash)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
##
-
# Extended validators are used to provide extra validation
-
# of submission. They are associated with request types
-
# and will import validation behaviour into submission.
-
#
-
# behaviour => the module that will be included in the validator, must respond to validate(submission)
-
# options => serialized hash for configuration
-
-
1
class ExtendedValidator < ActiveRecord::Base
-
-
-
1
class RequestTypeExtendedValidator < ActiveRecord::Base
-
-
1
self.table_name=('request_types_extended_validators')
-
-
1
belongs_to :extended_validator
-
1
belongs_to :request_type
-
1
validates_presence_of :extended_validator
-
1
validates_presence_of :request_type
-
-
end
-
-
1
after_initialize :import_behaviour
-
-
1
def import_behaviour
-
return if behaviour.nil?
-
behavior_module = "ExtendedValidator::#{behaviour}".constantize
-
self.class_eval do
-
include(behavior_module)
-
end
-
end
-
-
1
has_many :request_type_extened_validators, :dependent => :destroy, :class_name => 'ExtendedValidator::RequestTypeExtendedValidator'
-
1
has_many :request_types, :through => :request_type_extened_validators
-
-
1
validates_presence_of :behaviour
-
1
serialize :options
-
-
1
scope :for_submission, ->(submission) {
-
{
-
:joins => 'INNER JOIN request_types_extended_validators ON request_types_extended_validators.extended_validator_id = extended_validators.id',
-
:conditions => {:request_types_extended_validators => { :request_type_id => submission.request_types }}
-
}
-
}
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module ExtendedValidator::SpeciesValidator
-
-
1
def validate_order(order)
-
bad_samples = order.all_samples.select {|s| s.sample_metadata.sample_taxon_id != options[:taxon_id] }
-
return true if bad_samples.empty?
-
order.errors.add(:samples,"should have taxon_id #{options[:taxon_id]}: problems with #{bad_samples.map(&:sanger_sample_id).to_sentence}.")
-
false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# This class doesn't inherit from either library creation class because most of the behaviour is unwanted.
-
# For example, we don't know the read length etc. when the request is created
-
1
class ExternalLibraryCreationRequest < SystemRequest
-
-
1
redefine_state_machine do
-
# We have a vastly simplified two state state machine. Requests are passed once the manifest is processed
-
1
aasm_column :state
-
1
aasm_state :pending
-
1
aasm_state :passed, :enter => :on_passed
-
1
aasm_initial_state :pending
-
-
1
aasm_event :manifest_processed do
-
1
transitions :to => :passed, :from => [:pending]
-
end
-
end
-
-
1
def on_passed
-
perform_transfer_of_contents
-
end
-
-
1
def perform_transfer_of_contents
-
target_asset.aliquots << asset.aliquots.map(&:dup)
-
target_asset.save!
-
end
-
1
private :perform_transfer_of_contents
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ExternalProperty < ActiveRecord::Base
-
1
belongs_to :propertied, :polymorphic => true
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
class ExternalReleaseEvent < Event
-
1
after_create :physically_send_email, :if => :send_email
-
-
1
attr_accessor :send_email
-
-
1
def self.create_for_asset!(asset, sendmail = false)
-
self.create!(
-
:eventful => asset,
-
:message => "Data to be released externally set #{asset.external_release}",
-
:created_by => "", :family => "update", :of_interest_to => "administrators",
-
:send_email => sendmail
-
)
-
end
-
-
1
def set_qc_state
-
# This method should be empty!
-
end
-
-
1
def physically_send_email
-
study = Asset.find(self.eventful_id).studies.map do |study|
-
EventfulMailer.deliver_confirm_external_release_event(study.mailing_list_of_managers.reject(&:blank?), self.eventful, self.message, self.content, "No Milestone")
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class FacultySponsor < ActiveRecord::Base
-
1
extend Attributable::Association::Target
-
-
1
default_scope :order => :name
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :message => "of faculty sponsor already present in database"
-
-
1
def count_studies
-
Study.count(:joins => { :study_metadata => :faculty_sponsor }, :conditions => { :study_metadata => { :faculty_sponsor_id => self.id } })
-
end
-
-
1
def studies
-
Study.find(:all, :joins => { :study_metadata => :faculty_sponsor }, :conditions => { :study_metadata => { :faculty_sponsor_id => self.id } })
-
end
-
-
1
module Associations
-
1
def self.included(base)
-
1
base.validates_presence_of :faculty_sponsor_id
-
1
base.belongs_to :faculty_sponsor
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Failure < ActiveRecord::Base
-
1
belongs_to :failable, :polymorphic => true
-
1
after_create :notify_remote
-
-
1
def notify_remote
-
if self.notify_remote?
-
#Send event to Studies here
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Family < ActiveRecord::Base
-
-
1
belongs_to :task
-
1
belongs_to :workflow, :class_name => "LabInterface::Workflow", :foreign_key => :pipeline_workflow_id
-
1
has_many :assets
-
-
1
acts_as_descriptable :active
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
# There no subclasses at the moment, because we want to keep things simple
-
# # (especially no need to use a factory)
-
1
class FieldInfo
-
1
SELECTION = 'Selection'
-
1
TEXT = 'Text'
-
1
BOOLEAN = 'Boolean'
-
1
NUMERIC = 'Numeric'
-
1
Kind = [ SELECTION, TEXT, BOOLEAN ]
-
1
attr_accessor :display_name, :key, :kind, :default_value, :parameters
-
-
1
def initialize(args)
-
self.display_name = args.delete(:display_name) || args.delete("display_name" )
-
self.key = args.delete(:key) || args.delete("key")
-
self.kind = args.delete(:kind) || args.delete("kind")
-
self.default_value = args.delete(:default_value) || args.delete("default_value")
-
params = args.delete(:parameters) || args.delete("parameters")
-
args.merge!(params) if params
-
self.parameters= args
-
end
-
-
1
def value
-
return default_value || ""
-
end
-
-
# the following methods are Selection related. Move them in a subclass if needed
-
1
def selection
-
return nil unless kind == SELECTION
-
return self.parameters[:selection]
-
end
-
-
1
def set_selection(selection)
-
self.kind = SELECTION
-
self.parameters[:selection] = selection
-
end
-
-
1
def reapply(object)
-
value = nil
-
value = object.send(self.key) unless object.nil? or self.key.blank?
-
value ||= self.default_value
-
self.default_value = value
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class FlexibleCherrypickPipeline < CherrypickForPulldownPipeline
-
-
1
def post_finish_batch(batch, user)
-
batch.requests.with_target.each(&:pass!)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
##
-
# FlexibleSubmissions allow multiplexing based on
-
# pooling properties defined on the multiplexed request type
-
1
class FlexibleSubmission < Order
-
1
include Submission::FlexibleRequestGraph::OrderMethods
-
1
include Submission::Crossable
-
-
1
def request_type_ids=(id_list)
-
self.request_type_ids_list = id_list.map {|i| [i] }
-
end
-
-
1
def request_type_ids
-
request_type_ids_list.map(&:first)
-
end
-
-
1
def request_type_multiplier(&block)
-
return nil if request_types.blank?
-
mxr = RequestType.find(:all,:conditions=>{:id=>request_types,:for_multiplexing=>true}).each do |mx_request|
-
yield(request_types[request_types.index(mx_request.id)+1].to_s.to_sym)
-
end
-
yield(request_types.first.to_s.to_sym) if mxr.empty?
-
nil
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class FluidigmFile
-
-
1
module Finder
-
-
1
class Directory
-
1
def initialize(barcode)
-
@barcode = barcode
-
end
-
1
def empty?
-
!File.exists?("#{configatron.fluidigm_data.directory}/#{@barcode}/#{@barcode}.csv")
-
end
-
1
def content
-
file_content = nil
-
File.open("#{configatron.fluidigm_data.directory}/#{@barcode}/#{@barcode}.csv") do |file|
-
file_content = file.read
-
end
-
file_content
-
end
-
end
-
-
1
class Irods
-
1
def initialize(barcode)
-
@data = IrodsReader::DataObj.find('seq','dcterms:audience'=>configatron.irods_audience, :fluidigm_plate=>barcode)
-
end
-
-
1
def empty?
-
@data.empty?
-
end
-
-
1
def content(index=nil)
-
raise StandardError, "Multiple files found" if data.size > 1 && index.nil?
-
@data[index||0].retrive
-
end
-
end
-
-
1
def self.default
-
{
-
'irods' => Irods,
-
'directory' => Directory
-
}.fetch(configatron.fluidigm_data.source)
-
end
-
-
1
def self.find(barcode)
-
default.new(barcode)
-
end
-
-
end
-
-
1
class InvalidFile < StandardError; end
-
-
1
class Assay
-
-
1
attr_reader :name, :result
-
-
1
@@valid_markers = ['XX','XY','YY']
-
1
@@gender_map = {'XX' => 'F', 'YY' => 'F', 'XY' => 'M'}
-
-
1
def initialize(name,result)
-
@name = name
-
@result = result
-
end
-
-
1
def gender_marker?
-
/^GS/===name
-
end
-
-
1
def gender
-
@@gender_map[result]||'Unknown'
-
end
-
-
1
def pass?
-
@@valid_markers.include?(result)
-
end
-
end
-
-
1
class FluidigmWell
-
-
1
attr_reader :description
-
-
1
def initialize(description)
-
@description = description
-
end
-
-
1
def gender_markers
-
marker_array.select {|m| m.gender_marker?}.map(&:gender)
-
end
-
-
1
def add_assay(assay,marker)
-
marker_array << Assay.new(assay,marker)
-
end
-
-
1
def count
-
marker_array.select {|m| m.pass? }.count
-
end
-
-
1
private
-
-
1
def marker_array
-
@gender_markers ||= []
-
end
-
-
end
-
-
1
def initialize(file_contents)
-
@csv = CSV.parse(file_contents)
-
build_wells
-
self
-
end
-
-
1
def each_well
-
@wells.each {|_,w| yield(w)}
-
end
-
-
1
def for_plate?(test_plate)
-
plate_barcode == test_plate
-
end
-
-
1
def plate_barcode
-
@csv[0][2]
-
end
-
-
1
def well_at(description)
-
@wells ||= Hash.new {|hash,desc| hash[desc] = FluidigmWell.new(desc) }
-
@wells[description]
-
end
-
-
1
def well_locations
-
@wells.keys
-
end
-
-
1
private
-
1
def header_start_index
-
@header_start_index ||= (0..@csv.size).detect {|i| @csv[i][0]=='Experiment Information'} || raise(InvalidFile,'Could not find header')
-
end
-
-
1
def data_start_index
-
header_start_index+3
-
end
-
-
1
def headers
-
@headers ||= @csv[header_start_index].zip(@csv[header_start_index+1]).zip(@csv[header_start_index+2]).map {|h| h.join(' ')}
-
end
-
-
1
def column(head)
-
headers.index(head)
-
end
-
-
1
def build_wells
-
(data_start_index...@csv.size).each do |row_index|
-
row = @csv[row_index]
-
next if row[column('Experiment Information Sample Name')] == 'Water'
-
well = well_at(row[column('Experiment Information Chamber ID')].split('-').first)
-
well.add_assay(row[column('Experiment Information SNP Assay and Allele Names Assay')],row[column('Results Call Information Final')])
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class FluidigmTemplateTask < PlateTemplateTask
-
-
1
def partial
-
"fluidigm_template_batches"
-
end
-
-
1
def plate_purpose_options(batch)
-
requests = batch.requests.map { |r| r.submission ? r.submission.next_requests(r) : [] }.flatten
-
plate_purposes = requests.map(&:request_type).compact.uniq.map(&:acceptable_plate_purposes).flatten.uniq
-
plate_purposes = batch.requests.map { |r| r.request_metadata.target_purpose }.compact.uniq if plate_purposes.empty? # Fallback situation for the moment
-
plate_purposes.map { |p| [p.name, p.size, p.id] }.sort
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Fragment < Asset
-
1
include LocationAssociation::Locatable
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class GelDilutionPlate < WorkingDilutionPlate
-
1
self.prefix = "GD"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class GenerateManifestsTask < Task
-
-
1
def self.generate_manifests(batch,study)
-
ManifestGenerator.generate_manifests(batch,study)
-
end
-
-
1
def partial
-
"generate_manifests"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_generate_manifest_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
class GenotypingPipeline < Pipeline
-
1
include Pipeline::InboxGroupedBySubmission
-
1
INBOX_PARTIAL = 'group_by_parent'
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
1
def genotyping?
-
true
-
end
-
-
# Pipelines in Genotyping do not require their batches to record the position of the requests.
-
1
def requires_position?
-
false
-
end
-
-
1
def request_actions
-
[:fail,:remove]
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class GenotypingRequest < CustomerRequest
-
1
include Request::HasNoTargetAsset
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Health
-
-
1
attr_reader :status, :message
-
-
1
def initialize
-
@status, @message = :ok, []
-
self.check
-
end
-
-
1
def check
-
@message << 'No problems detected.' if status == :ok
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014 Genome Research Ltd.
-
1
class HiSeqSequencingRequest < SequencingRequest
-
1
include Request::CustomerResponsibility
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Identifier < ActiveRecord::Base
-
1
validates_presence_of :resource_name, :identifiable_id
-
1
validates_uniqueness_of :external_id, :scope => [:identifiable_id, :resource_name] # only one external per asset per resource
-
-
1
belongs_to :identifiable, :polymorphic => true
-
1
belongs_to :external, :polymorphic => true
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
class IlluminaB::CovarisPlatePurpose < IlluminaHtp::CovarisPlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
class IlluminaB::FinalPlatePurpose < IlluminaHtp::FinalPlatePurpose
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class IlluminaB::InitialStockTubePurpose < IlluminaHtp::InitialStockTubePurpose
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
class IlluminaB::MxTubePurpose < IlluminaHtp::MxTubePurpose
-
1
def stock_plate(tube)
-
tube.requests_as_target.where_is_a?(IlluminaB::Requests::StdLibraryRequest).first.asset.plate
-
end
-
-
1
def request_state(request,state)
-
mappings = {'cancelled' =>'cancelled','failed' => 'failed','passed' => 'passed'}
-
request.is_a?(TransferRequest) ? state : mappings[state]
-
end
-
1
private :request_state
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
class IlluminaB::PcrPlatePurpose < PlatePurpose
-
1
alias_method(:default_transition_to, :transition_to)
-
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility=false)
-
nudge_parent_plate(plate, state, user, contents)
-
default_transition_to(plate, state, user, contents, customer_accepts_responsibility)
-
end
-
-
1
def nudge_parent_plate(plate, state, user, contents)
-
case state
-
when 'started_fx' then plate.parent.transition_to('started', user, contents)
-
when 'started_mj' then plate.parent.transition_to('passed', user, contents)
-
end
-
end
-
1
private :nudge_parent_plate
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
-
1
module IlluminaB::PlatePurposes
-
1
PLATE_PURPOSE_FLOWS = [
-
[
-
'ILB_STD_INPUT',
-
'ILB_STD_COVARIS',
-
'ILB_STD_SH',
-
'ILB_STD_PREPCR',
-
'ILB_STD_PCR',
-
'ILB_STD_PCRR',
-
'ILB_STD_PCRXP',
-
'ILB_STD_PCRRXP'
-
]
-
]
-
-
1
TUBE_PURPOSE_FLOWS = [
-
[
-
'ILB_STD_STOCK',
-
'ILB_STD_MX'
-
]
-
]
-
-
1
BRANCHES = [
-
[ 'ILB_STD_INPUT', 'ILB_STD_COVARIS', 'ILB_STD_SH', 'ILB_STD_PREPCR', 'ILB_STD_PCR', 'ILB_STD_PCRXP', 'ILB_STD_STOCK', 'ILB_STD_MX' ],
-
[ 'ILB_STD_PREPCR', 'ILB_STD_PCRR', 'ILB_STD_PCRRXP', 'ILB_STD_STOCK' ]
-
]
-
-
1
STOCK_PLATE_PURPOSE = 'ILB_STD_INPUT'
-
-
# Don't have ILllumina B QC plates at the momnet...
-
1
PLATE_PURPOSE_LEADING_TO_QC_PLATES = [
-
]
-
-
1
STOCK_PLATE_PURPOSE_TO_OUTER_REQUEST = {
-
'ILB_STD_INPUT' => 'illumina_b_std'
-
}
-
-
1
OUTPUT_PLATE_PURPOSES = []
-
-
1
PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES = [
-
[ 'ILB_STD_INPUT', 'ILB_STD_COVARIS','IlluminaB::Requests::InputToCovaris' ],
-
[ 'ILB_STD_COVARIS', 'ILB_STD_SH', 'IlluminaB::Requests::CovarisToSheared' ],
-
[ 'ILB_STD_PREPCR', 'ILB_STD_PCR', 'IlluminaB::Requests::PrePcrToPcr' ],
-
[ 'ILB_STD_PREPCR', 'ILB_STD_PCRR', 'IlluminaB::Requests::PrePcrToPcr' ],
-
[ 'ILB_STD_PCR', 'ILB_STD_PCRXP', 'IlluminaB::Requests::PcrToPcrXp' ],
-
[ 'ILB_STD_PCRR', 'ILB_STD_PCRRXP', 'IlluminaB::Requests::PcrToPcrXp' ],
-
[ 'ILB_STD_PCRXP', 'ILB_STD_STOCK', 'IlluminaB::Requests::PcrXpToStock' ],
-
[ 'ILB_STD_PCRRXP', 'ILB_STD_STOCK', 'IlluminaB::Requests::PcrXpToStock' ]
-
]
-
-
1
PLATE_PURPOSE_TYPE = {
-
'ILB_STD_INPUT' => IlluminaB::StockPlatePurpose,
-
'ILB_STD_COVARIS' => IlluminaB::CovarisPlatePurpose,
-
'ILB_STD_SH' => PlatePurpose,
-
'ILB_STD_PREPCR' => PlatePurpose,
-
'ILB_STD_PCR' => IlluminaB::PcrPlatePurpose,
-
'ILB_STD_PCRXP' => IlluminaB::FinalPlatePurpose,
-
'ILB_STD_PCRR' => PlatePurpose,
-
'ILB_STD_PCRRXP' => IlluminaB::FinalPlatePurpose,
-
'ILB_STD_STOCK' => IlluminaB::StockTubePurpose,
-
'ILB_STD_MX' => IlluminaB::MxTubePurpose
-
}
-
-
1
def self.request_type_prefix
-
"Illumina-B"
-
end
-
-
1
extend IlluminaHtp::PlatePurposes::PurposeHelpers
-
-
end
-
-
-
# We require all the plate and tube purpose files here as Rails eager loading does not play nicely with single table
-
# inheritance
-
-
1
['covaris_plate','final_plate','initial_stock_tube','mx_tube','pcr_plate','post_shear_qc_plate','stock_plate','stock_tube'].each do |type|
-
8
require "#{Rails.root.to_s}/app/models/illumina_b/#{type}_purpose"
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class IlluminaB::PostShearQcPlatePurpose < IlluminaHtp::PostShearQcPlatePurpose
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014 Genome Research Ltd.
-
1
module IlluminaB::Requests
-
-
1
class StdLibraryRequest < Request::LibraryCreation
-
1
fragment_size_details(:no_default, :no_default)
-
end
-
-
1
class InputToCovaris < TransferRequest
-
1
include TransferRequest::InitialTransfer
-
end
-
-
1
class CovarisToSheared < IlluminaHtp::Requests::CovarisToSheared
-
end
-
-
1
class PrePcrToPcr < IlluminaHtp::Requests::PrePcrToPcr
-
end
-
-
1
class PcrToPcrXp < IlluminaHtp::Requests::PcrToPcrXp
-
end
-
-
1
class PcrXpToStock < IlluminaHtp::Requests::PcrXpToStock
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
-
1
class IlluminaB::StockPlatePurpose < IlluminaHtp::StockPlatePurpose
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
class IlluminaB::StockTubePurpose < IlluminaHtp::StockTubePurpose
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class IlluminaC::AlLibsPurpose < PlatePurpose
-
1
include PlatePurpose::Initial
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class IlluminaC::AlLibsTaggedPurpose < PlatePurpose
-
1
include PlatePurpose::Initial
-
1
include PlatePurpose::Library
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014 Genome Research Ltd.
-
1
module IlluminaC::Helper
-
-
1
require 'hiseq_2500_helper'
-
-
1
ACCEPTABLE_REQUEST_TYPES = ['illumina_c_pcr','illumina_c_nopcr', 'illumina_c_multiplexing']
-
1
ACCEPTABLE_SEQUENCING_REQUESTS = [
-
'illumina_c_single_ended_sequencing',
-
'illumina_c_paired_end_sequencing',
-
'illumina_c_hiseq_paired_end_sequencing',
-
'illumina_c_single_ended_hi_seq_sequencing',
-
'illumina_c_miseq_sequencing',
-
'illumina_c_hiseq_2500_paired_end_sequencing',
-
'illumina_c_hiseq_2500_single_end_sequencing'
-
]
-
1
PIPELINE = 'Illumina-C'
-
1
class TemplateConstructor
-
-
# Construct submission templates for the generic pipeline
-
# opts is a hash
-
# {
-
# :name => The Name for the Library Step
-
# :sequencing => Optional array of sequencing request type keys. Default is all.
-
# :role => The role that will be printed on barcodes
-
# :type => 'illumina_c_pcr'||'illumina_c_nopcr'
-
# }
-
1
attr_accessor :name, :type, :role, :skip_cherrypick
-
1
attr_reader :sequencing
-
-
1
def initialize(params)
-
self.name = params[:name]
-
self.type = params[:type]
-
self.role = params[:role]
-
self.skip_cherrypick = params[:skip_cherrypick]
-
self.sequencing = params[:sequencing]||ACCEPTABLE_SEQUENCING_REQUESTS
-
end
-
-
1
def sequencing=(sequencing_array)
-
@sequencing = sequencing_array.map do |request|
-
RequestType.find_by_key!(request)
-
end
-
end
-
-
1
def validate!
-
[:name,:type,:role].each do |value|
-
raise "Must provide a #{value}" if send(value).nil?
-
end
-
raise "Request Type should be #{ACCEPTABLE_REQUEST_TYPES.join(', ')}" unless ACCEPTABLE_REQUEST_TYPES.include?(type)
-
true
-
end
-
-
1
def name_for(cherrypick,sequencing_request_type)
-
"#{PIPELINE} #{cherrypick ? "Cherrypicked - " : ''}#{name} - #{sequencing_request_type.name.gsub("#{PIPELINE} ",'')}"
-
end
-
-
1
def build!
-
validate!
-
each_submission_template do |config|
-
SubmissionTemplate.create!(config)
-
end
-
end
-
-
1
def cherrypick_id
-
RequestType.find_by_key!()
-
end
-
-
1
def request_type_ids(cherrypick,sequencing)
-
ids = cherrypick ? [[RequestType.find_by_key('cherrypick_for_illumina_c').id]] : []
-
ids << [RequestType.find_by_key(type).id] << [sequencing.id]
-
end
-
-
1
def cherrypick_options
-
[!skip_cherrypick,false].uniq
-
end
-
-
1
def each_submission_template
-
cherrypick_options.each do |cherrypick|
-
sequencing.each do |sequencing_request_type|
-
yield({
-
:name => name_for(cherrypick,sequencing_request_type),
-
:submission_class_name => 'LinearSubmission',
-
:submission_parameters => submission_parameters(cherrypick,sequencing_request_type),
-
:product_line_id => ProductLine.find_by_name(PIPELINE),
-
})
-
end
-
end
-
end
-
-
1
def submission_parameters(cherrypick,sequencing)
-
sp = {
-
:request_type_ids_list => request_type_ids(cherrypick,sequencing),
-
:workflow_id => Submission::Workflow.find_by_key('short_read_sequencing').id,
-
:order_role_id => Order::OrderRole.find_or_create_by_role(role).id,
-
:info_differential => Submission::Workflow.find_by_key('short_read_sequencing').id
-
}
-
return sp if ['illumina_c_single_ended_sequencing','illumina_c_paired_end_sequencing'].include?(sequencing.key) || type == 'illumina_c_multiplexing'
-
end
-
-
1
def sizes_for(sequencing)
-
{
-
'illumina_c_hiseq_2500_single_end_sequencing' => ["50"],
-
'illumina_c_hiseq_2500_paired_end_sequencing' => ["75", "100"],
-
'illumina_c_single_ended_hi_seq_sequencing' => ["50"],
-
'illumina_c_hiseq_paired_end_sequencing' => ["50", "75", "100"],
-
'illumina_c_miseq_sequencing' => ["25", "50", "130", "150", "250"]
-
}[sequencing.key] || raise("No settings fo3 #{sequencing.key}")
-
end
-
-
1
def library_types
-
RequestType.find_by_key(self.type).library_types.map(&:name)
-
end
-
-
1
def update!
-
each_submission_template do |options|
-
next if options[:submission_parameters][:input_field_infos].nil?
-
SubmissionTemplate.find_by_name!(options[:name]).update_attributes!(:submission_parameters=>options[:submission_parameters])
-
end
-
end
-
-
1
def self.find_for(name,sequencing=nil)
-
tc = TemplateConstructor.new(:name=>name, :sequencing=>sequencing)
-
[true,false].map do |cherrypick|
-
tc.sequencing.map do |sequencing_request_type|
-
SubmissionTemplate.find_by_name!(tc.name_for(cherrypick,sequencing_request_type))
-
end
-
end.flatten
-
end
-
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaC::LibPcrPurpose < PlatePurpose
-
1
include PlatePurpose::Library
-
-
1
alias_method(:default_transition_to, :transition_to)
-
-
1
def transition_to(plate, state, user, contents = nil,customer_accepts_responsibility=false)
-
nudge_parent_plate(plate, state, user, contents)
-
default_transition_to(plate, state, user, contents,customer_accepts_responsibility)
-
end
-
-
1
def nudge_parent_plate(plate, state, user, contents)
-
plate.parent.transition_to(state, user, contents) if ['started','passed'].include?(state)
-
end
-
1
private :nudge_parent_plate
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
1
class IlluminaC::LibPcrXpPurpose < PlatePurpose
-
-
1
include PlatePurpose::RequestAttachment
-
-
1
self.connect_on = 'qc_complete'
-
1
self.connected_class = IlluminaC::Requests::LibraryRequest
-
1
self.connect_downstream = false
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014 Genome Research Ltd.
-
1
class IlluminaC::MxTubePurpose < IlluminaHtp::MxTubePurpose
-
1
def created_with_request_options(tube)
-
library_request(tube).try(:request_options_for_creation) || {}
-
end
-
-
1
def stock_plate(tube)
-
lt = library_request(tube)
-
return lt.asset.plate if lt.present?
-
nil
-
end
-
-
1
def library_request(tube)
-
tube.requests_as_target.where_is_a?(IlluminaC::Requests::LibraryRequest).first||
-
tube.requests_as_target.where_is_a?(Request::Multiplexing).first.asset.
-
requests_as_target.where_is_a?(IlluminaC::Requests::LibraryRequest).first
-
end
-
-
1
def request_state(request,state)
-
mappings = {'cancelled' =>'cancelled','failed' => 'failed','passed' => 'passed'}
-
request.is_a?(TransferRequest)||request.is_a?(Request::Multiplexing) ? state : mappings[state]
-
end
-
1
private :request_state
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
-
1
module IlluminaC::PlatePurposes
-
1
PLATE_PURPOSE_FLOWS = [
-
[
-
'ILC Stock',
-
'ILC AL Libs',
-
'ILC Lib PCR',
-
'ILC Lib PCR-XP',
-
'ILC AL Libs Tagged'
-
]
-
]
-
-
1
TUBE_PURPOSE_FLOWS = [
-
[
-
'ILC Lib Pool Norm'
-
]
-
]
-
-
1
QC_TUBE = 'ILC QC Pool'
-
-
1
BRANCHES = [
-
['ILC Stock','ILC AL Libs','ILC Lib PCR','ILC Lib PCR-XP','ILC Lib Pool Norm'],
-
['ILC Stock','ILC AL Libs Tagged','ILC Lib Pool Norm']
-
]
-
-
1
STOCK_PLATE_PURPOSE = 'ILC Stock'
-
-
# We Don't have QC Plates
-
1
PLATE_PURPOSE_LEADING_TO_QC_TUBES = [
-
'ILC AL Libs Tagged',
-
'ILC Lib PCR-XP'
-
]
-
-
1
STOCK_PLATE_PURPOSE_TO_OUTER_REQUEST = {
-
'ILC Stock' => 'illumina_c_pcr',
-
'ILC Stock' => 'illumina_c_nopcr'
-
-
}
-
-
1
OUTPUT_PLATE_PURPOSES = []
-
-
1
PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES = [
-
[ 'ILC Stock', 'ILC AL Libs', 'IlluminaC::Requests::InitialTransfer' ],
-
[ 'ILC Stock', 'ILC AL Libs Tagged', 'IlluminaC::Requests::StockToAlLibsTagged' ],
-
[ 'ILC Lib PCR', 'ILC Lib PCR-XP', 'IlluminaC::Requests::QcCompleteable' ]
-
]
-
-
1
PLATE_PURPOSE_TYPE = {
-
'ILC QC Pool' => IlluminaC::QcPoolPurpose,
-
'ILC Stock' => IlluminaC::StockPurpose,
-
'ILC AL Libs' => IlluminaC::AlLibsPurpose,
-
'ILC Lib PCR' => IlluminaC::LibPcrPurpose,
-
'ILC Lib PCR-XP' => IlluminaC::LibPcrXpPurpose,
-
'ILC AL Libs Tagged' => IlluminaC::AlLibsTaggedPurpose,
-
'ILC Lib Pool Norm' => IlluminaC::MxTubePurpose
-
}
-
-
1
def self.request_type_prefix
-
"Illumina-C"
-
end
-
-
1
extend IlluminaHtp::PlatePurposes::PurposeHelpers
-
-
1
def self.create_qc_plates
-
nil
-
end
-
-
1
def self.create_tube_purposes
-
super
-
create_qc_tubes
-
end
-
-
1
def self.create_qc_tubes
-
ActiveRecord::Base.transaction do
-
qc_tube_purpose = purpose_for(self::QC_TUBE).create!(:name=>self::QC_TUBE, :target_type=>'QcTube',:barcode_printer_type => BarcodePrinterType.find_by_type('BarcodePrinterType1DTube'))
-
self::PLATE_PURPOSE_LEADING_TO_QC_TUBES.each do |name|
-
plate_purpose = Purpose.find_by_name(name) or raise StandardError, "Cannot find purpose #{name.inspect}"
-
plate_purpose.child_relationships.create!(:child => qc_tube_purpose, :transfer_request_type => RequestType.find_by_name('Transfer'))
-
end
-
end
-
end
-
-
end
-
-
# We require all the plate and tube purpose files here as Rails eager loading does not play nicely with single table
-
# inheritance
-
-
1
['al_libs','al_libs_tagged','lib_pcr','lib_pcr_xp','mx_tube','qc_pool','stock'].each do |type|
-
7
require "#{Rails.root.to_s}/app/models/illumina_c/#{type}_purpose"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaC::QcPoolPurpose < Tube::Purpose
-
-
1
def transition_to(tube, state, user, _ = nil, customer_accepts_responsibility=false)
-
ActiveRecord::Base.transaction do
-
tube.requests_as_target.all(not_terminated).each do |request|
-
request.transition_to(state)
-
end
-
end
-
end
-
-
1
def not_terminated
-
{:conditions=>[ 'state NOT IN (?)',['cancelled','failed','aborted']]}
-
end
-
1
private :not_terminated
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014 Genome Research Ltd.
-
1
module IlluminaC::Requests
-
-
1
class LibraryRequest < Request::LibraryCreation
-
1
def role; "#{request_metadata.library_type} #{order.role}"; end
-
-
# Pop the request type in the pool information
-
1
def update_pool_information(pool_information)
-
super
-
pool_information[:request_type] = request_type.name
-
end
-
-
end
-
-
1
class PcrLibraryRequest < LibraryRequest
-
1
fragment_size_details(:no_default, :no_default)
-
end
-
-
1
class NoPcrLibraryRequest < LibraryRequest
-
1
fragment_size_details(:no_default, :no_default)
-
end
-
-
1
class InitialTransfer < TransferRequest
-
1
include TransferRequest::InitialTransfer
-
end
-
-
1
class QcCompleteable < TransferRequest
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :passed
-
1
aasm_state :qc_complete
-
1
aasm_state :failed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started, :failed] end
-
2
aasm_event :qc do transitions :to => :qc_complete, :from => [:passed] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started, :passed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed] end
-
end
-
end
-
-
1
class StockToAlLibsTagged < QcCompleteable
-
1
include TransferRequest::InitialTransfer
-
end
-
-
-
1
module Helpers
-
-
1
def create_request_types
-
each_request_type do |params|
-
RequestType.create!(params)
-
end
-
IlluminaC::PlatePurposes::STOCK_PLATE_PURPOSE_TO_OUTER_REQUEST.each do |purpose,request|
-
RequestType.find_by_key(request).acceptable_plate_purposes << Purpose.find_by_name(purpose)
-
end
-
end
-
-
1
def destroy_request_types
-
each_request_type do |params|
-
RequestType.find_by_name(params[:name]).destroy
-
end
-
end
-
-
1
def each_request_type
-
[
-
{
-
:name => 'Illumina-C Library Creation PCR',
-
:key => 'illumina_c_pcr',
-
:for_multiplexing =>true,
-
:request_class_name =>'IlluminaC::Requests::PcrLibraryRequest',
-
:target_purpose =>Purpose.find_by_name('ILC Lib Pool Norm')
-
},
-
{
-
:name => 'Illumina-C Library Creation No PCR',
-
:key => 'illumina_c_nopcr',
-
:for_multiplexing =>true,
-
:request_class_name =>'IlluminaC::Requests::NoPcrLibraryRequest',
-
:target_purpose =>Purpose.find_by_name('ILC Lib Pool Norm')
-
},
-
{
-
:name => 'Illumina-C Library Creation PCR No Pooling',
-
:key => 'illumina_c_pcr_no_pool',
-
:request_class_name => 'IlluminaC::Requests::PcrLibraryRequest',
-
:for_multiplexing => false
-
},
-
{
-
:name => 'Illumina-C Multiplexing',
-
:key => 'illumina_c_multiplexing',
-
:request_class_name => 'Request::Multiplexing',
-
:for_multiplexing => true,
-
:target_purpose =>Purpose.find_by_name('ILC Lib Pool Norm')
-
}
-
].each do |params|
-
params.merge!({
-
:workflow => Submission::Workflow.find_by_name("Next-gen sequencing"),
-
:asset_type => 'Well',
-
:order =>1,
-
:initial_state =>'pending',
-
:billable =>true,
-
:product_line => ProductLine.find_by_name('Illumina-C')
-
})
-
yield(params)
-
end
-
end
-
-
end
-
1
extend Helpers
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaC::StockPurpose < PlatePurpose
-
1
include PlatePurpose::Stock
-
-
1
def transition_to(plate, state, user, contents = nil,customer_accepts_responsibility=false)
-
return unless ['failed','cancelled'].include?(state)
-
plate.wells.located_at(contents).include_requests_as_target.include_requests_as_source.each do |well|
-
well.requests.each {|r| r.send(transition_from(r.state)) if r.is_a?(IlluminaC::Requests::LibraryRequest) && transition_from(r.state) }
-
well.requests_as_target.each {|r| r.transition_to('failed') if r.is_a?(TransferRequest)}
-
end
-
end
-
-
1
def transition_from(state)
-
{'pending' => :cancel_before_started!, 'started'=>:cancel!}[state]
-
end
-
1
private :transition_from
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class IlluminaHtp::CovarisPlatePurpose < PlatePurpose
-
1
include PlatePurpose::Initial
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class IlluminaHtp::DownstreamPlatePurpose < PlatePurpose
-
-
1
def source_wells_for(stock_wells)
-
Well.in_column_major_order.stock_wells_for(stock_wells)
-
end
-
-
1
def library_source_plates(plate)
-
super.map {|s| s.source_plates }.flatten.uniq
-
end
-
-
1
def library_source_plate(plate)
-
super.source_plate
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaHtp::FinalPlatePurpose < PlatePurpose
-
1
include PlatePurpose::Library
-
-
1
alias_method(:default_transition_to, :transition_to)
-
-
1
def transition_to(plate, state, user, contents = nil,customer_accepts_responsibility=false)
-
nudge_pre_pcr_wells(plate, state, user, contents,customer_accepts_responsibility)
-
default_transition_to(plate, state, user, contents,customer_accepts_responsibility)
-
end
-
-
1
def attatched?(plate)
-
plate.state == ('qc_complete')
-
end
-
-
1
def fail_stock_well_requests(wells,_)
-
# Handled by the nudge of the pre PCR wells!
-
end
-
1
private :fail_stock_well_requests
-
-
1
def nudge_pre_pcr_wells(plate, state, user, contents,customer_accepts_responsibility)
-
plate.parent.parent.transition_to(state, user, contents,customer_accepts_responsibility) if state == 'failed'
-
end
-
1
private :nudge_pre_pcr_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaHtp::InitialDownstreamPlatePurpose < IlluminaHtp::DownstreamPlatePurpose
-
# Initial plates in the pulldown pipelines change the state of the pulldown requests they are being
-
# created for to exactly the same state.
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
ActiveRecord::Base.transaction do
-
super
-
new_outer_state = ['started','passed','qc_complete','nx_in_progress'].include?(state) ? 'started' : state
-
-
# CAUTION!
-
# TODO: While the behaviour here wont cause us any issues, its actually subtly wrong.
-
# 1) Multiple wells on the same plate may have the same stock wells
-
# 2) Well location may change between parent and child plates.
-
# 3) As we only fire on pending requests this isn't actually a massive problem as we'll be targeting the whole plate anyway
-
active_submissions = plate.submission_ids
-
-
stock_wells(plate,contents).each do |source_well|
-
# Only transitions from last submission
-
source_well.requests.select {|r| r.library_creation? && active_submissions.include?(r.submission_id) }.each do |request|
-
request.transition_to(new_outer_state) if request.pending?
-
end
-
end
-
end
-
end
-
-
1
def stock_wells(plate,contents)
-
return plate.parent.wells unless contents.present?
-
plate.parent.wells.located_at(contents)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaHtp::InitialStockTubePurpose < IlluminaHtp::StockTubePurpose
-
-
1
module InitialTube
-
-
1
def valid_transition?(outer_request,target_state)
-
target_state!='started'||outer_request.pending?
-
end
-
-
1
def transition_to(tube, state, user, _ = nil, customer_accepts_responsibility = false)
-
ActiveRecord::Base.transaction do
-
tube.requests_as_target.all(not_terminated).each do |request|
-
request.transition_to(state)
-
new_outer_state = ['started','passed','qc_complete'].include?(state) ? 'started' : state
-
request.outer_request.customer_accepts_responsibility! if customer_accepts_responsibility
-
request.outer_request.transition_to(new_outer_state) if valid_transition?(request.outer_request,new_outer_state)
-
end
-
end
-
end
-
-
##
-
# We find sibling tubes by first finding the outer request type (library completion) and the transfer request type
-
# We find all outer requests of the same type in the submission, and match these up with the transfer requests
-
# The RIGHT OUTER JOIN ensures we have a null result for any outer requests which don't have matching transfer requests
-
# We only pick up open requests, just in case a whole tube has failed / been cancelled.
-
1
def sibling_tubes(tube)
-
return [] if tube.submission.nil?
-
submission_id = tube.submission.id
-
tfr_request_type = tube.requests_as_target.first.request_type_id
-
outr_request_type = tube.requests_as_target.first.outer_request.request_type_id
-
-
siblings = Asset.find(:all,
-
:select => 'DISTINCT assets.*, tfr.state AS quick_state',
-
:joins => [
-
'LEFT JOIN requests AS tfr ON tfr.target_asset_id = assets.id AND tfr.request_type_id = %i AND tfr.submission_id = %i' % [tfr_request_type, submission_id],
-
'RIGHT OUTER JOIN requests AS outr ON outr.asset_id = tfr.asset_id AND outr.asset_id IS NOT NULL'
-
],
-
:conditions => ['outr.submission_id = ? AND outr.request_type_id = ? AND outr.state IN (?)', submission_id, outr_request_type,Request::Statemachine::OPENED_STATE],
-
:include => [:uuid_object, :barcode_prefix]
-
)
-
siblings.map {|s| s.id.nil? ? :no_tube : {:name=>s.name,:uuid=>s.uuid,:ean13_barcode=>s.ean13_barcode,:state=>s.quick_state} }
-
end
-
-
end
-
-
1
include InitialTube
-
-
end
-
-
1
class IlluminaHtp::LibraryCompleteOnQcPurpose < PlatePurpose
-
-
1
include PlatePurpose::RequestAttachment
-
1
include PlatePurpose::BroadcastLibraryComplete
-
-
1
self.connect_on = 'qc_complete'
-
1
self.connect_downstream = false
-
1
self.connected_class = IlluminaHtp::Requests::StdLibraryRequest
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class IlluminaHtp::MxTubeNoQcPurpose < IlluminaHtp::MxTubePurpose
-
-
1
def mappings
-
{'cancelled' =>'cancelled','failed' => 'failed','passed' => 'passed'}
-
end
-
1
private :mappings
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
1
class IlluminaHtp::MxTubePurpose < Tube::Purpose
-
1
def created_with_request_options(tube)
-
tube.requests_as_target.where_is_a?(Request::LibraryCreation).first.try(:request_options_for_creation) || {}
-
end
-
-
1
def transition_to(tube, state, user, _ = nil, customer_accepts_responsibility = false)
-
orders = Set.new
-
target_requests(tube).each do |request|
-
request.customer_accepts_responsibility! if customer_accepts_responsibility
-
to_state = request_state(request,state)
-
request.transition_to(to_state) unless to_state.nil?
-
orders << request.order.id unless request.is_a?(TransferRequest)
-
end
-
generate_events_for(tube,orders,user) if state == 'qc_complete'
-
end
-
-
1
def target_requests(tube)
-
tube.requests_as_target.for_billing.all(
-
:conditions=>[
-
"state IN (?) OR (state='passed' AND sti_type IN (?))",
-
Request::Statemachine::OPENED_STATE,
-
TransferRequest.descendants.map(&:to_s)
-
])
-
end
-
1
private :target_requests
-
-
1
def stock_plate(tube)
-
tube.requests_as_target.where_is_a?(Request::LibraryCreation).detect{|r| r.asset.present?}.asset.plate
-
end
-
-
1
def library_source_plates(tube)
-
Plate.find(:all,
-
:select=>'DISTINCT assets.*',
-
:joins=>{:wells=>:requests},
-
:conditions=>[
-
'requests.target_asset_id = ? AND requests.sti_type IN (?)',
-
tube.id,
-
[Request::LibraryCreation,*Request::LibraryCreation.descendants].map(&:name)
-
]
-
).map(&:source_plate)
-
end
-
-
1
def request_state(request,state)
-
request.is_a?(TransferRequest) ? state : mappings[state]
-
end
-
1
private :request_state
-
-
1
def mappings
-
{'cancelled' =>'cancelled','failed' => 'failed','qc_complete' => 'passed'}
-
end
-
1
private :mappings
-
-
1
def generate_events_for(tube,orders,user)
-
orders.each do |order_id|
-
BroadcastEvent::LibraryComplete.create!(:seed=>tube,:user=>user,:properties=>{:order_id=>order_id})
-
end
-
end
-
1
private :generate_events_for
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class IlluminaHtp::NormalizedPlatePurpose < PlatePurpose
-
1
include PlatePurpose::RequestAttachment
-
1
include PlatePurpose::BroadcastLibraryComplete
-
-
1
self.connect_on = 'passed'
-
1
self.connect_downstream = false
-
1
self.connected_class = IlluminaHtp::Requests::LibraryCompletion
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
1
module IlluminaHtp::PlatePurposes
-
1
PLATE_PURPOSE_FLOWS = [
-
[
-
'Cherrypicked',
-
'Shear',
-
'Post Shear',
-
'AL Libs',
-
'Lib PCR',
-
'Lib PCRR',
-
'Lib PCR-XP',
-
'Lib PCRR-XP',
-
# Alternative branch for ILA
-
'Post Shear XP',
-
# Plate based pooling
-
'Lib Norm',
-
'Lib Norm 2',
-
'Lib Norm 2 Pool'
-
],
-
[
-
'PF Cherrypicked',
-
'PF Shear',
-
'PF Post Shear',
-
'PF Post Shear XP',
-
'PF Lib',
-
'PF Lib XP',
-
'PF Lib XP2',
-
'PF EM Pool',
-
'PF Lib Norm'
-
]
-
]
-
-
1
TUBE_PURPOSE_FLOWS = [
-
[
-
'Lib Pool',
-
'Lib Pool Norm',
-
],
-
[
-
'Lib Pool Pippin',
-
'Lib Pool Conc',
-
'Lib Pool SS',
-
'Lib Pool SS-XP',
-
'Lib Pool SS-XP-Norm'
-
],
-
[
-
'Cap Lib Pool Norm'
-
],
-
[
-
'PF MiSeq Stock',
-
'PF MiSeq QC'
-
],
-
['PF MiSeq QCR']
-
]
-
-
1
BRANCHES = [
-
[ 'PF Cherrypicked', 'PF Shear', 'PF Post Shear', 'PF Post Shear XP', 'PF Lib', 'PF Lib XP', 'PF Lib XP2', 'PF EM Pool', 'PF Lib Norm'],
-
[ 'PF Lib XP2', 'PF MiSeq Stock', 'PF MiSeq QC'],
-
[ 'PF MiSeq Stock','PF MiSeq QCR'],
-
[ 'Cherrypicked', 'Shear', 'Post Shear', 'AL Libs', 'Lib PCR', 'Lib PCR-XP','Lib Pool','Lib Pool Norm'],
-
[ 'Lib PCR-XP','Lib Pool Pippin', 'Lib Pool Conc', 'Lib Pool SS', 'Lib Pool SS-XP', 'Lib Pool SS-XP-Norm' ],
-
[ 'Lib PCRR', 'Lib PCRR-XP','Lib Pool Pippin' ],
-
[ 'Lib PCR-XP','ISC lib pool' ],
-
[ 'Lib PCR-XP','Lib Norm','Lib Norm 2','Lib Norm 2 Pool'],
-
[ 'Lib PCRR-XP','ISC lib pool' ],
-
[ 'Post Shear', 'Post Shear XP', 'AL Libs']
-
]
-
-
1
STOCK_PLATE_PURPOSE = 'Cherrypicked'
-
-
1
OUTPUT_PLATE_PURPOSES = ['Lib PCR-XP','Lib PCRR-XP']
-
-
1
PLATE_PURPOSE_LEADING_TO_QC_PLATES = [
-
'Post Shear', 'Lib PCR-XP', 'Lib PCRR-XP', 'Lib Norm', 'PF EM Pool'
-
]
-
-
1
STOCK_PLATE_PURPOSE_TO_OUTER_REQUEST = {
-
'Cherrypicked' => 'illumina_b_shared'
-
}
-
-
1
PF_PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES = [
-
['PF Cherrypicked', 'PF Shear', 'IlluminaHtp::Requests::CherrypickedToShear'],
-
['PF Shear', 'PF Post Shear'],
-
['PF Post Shear', 'PF Post Shear XP'],
-
['PF Post Shear XP', 'PF Lib XP'],
-
['PF Lib XP', 'PF Lib XP2']
-
]
-
-
1
PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES = [
-
[ 'PF Cherrypicked', 'PF Shear', 'IlluminaHtp::Requests::CherrypickedToShear' ],
-
[ 'Cherrypicked', 'Shear', 'IlluminaHtp::Requests::CherrypickedToShear' ],
-
[ 'Shear', 'Post Shear', 'IlluminaHtp::Requests::CovarisToSheared' ],
-
[ 'Post Shear', 'AL Libs', 'IlluminaHtp::Requests::PostShearToAlLibs' ],
-
[ 'Post Shear XP', 'AL Libs', 'IlluminaHtp::Requests::PostShearToAlLibs' ],
-
[ 'AL Libs', 'Lib PCR', 'IlluminaHtp::Requests::PrePcrToPcr' ],
-
[ 'AL Libs', 'Lib PCRR', 'IlluminaHtp::Requests::PrePcrToPcr' ],
-
[ 'Lib PCR', 'Lib PCR-XP', 'IlluminaHtp::Requests::PcrToPcrXp' ],
-
[ 'Lib PCRR', 'Lib PCRR-XP', 'IlluminaHtp::Requests::PcrToPcrXp' ],
-
[ 'Lib PCR-XP', 'Lib Pool', 'IlluminaHtp::Requests::PcrXpToPool' ],
-
[ 'Lib PCRR-XP', 'Lib Pool', 'IlluminaHtp::Requests::PcrXpToPool' ],
-
[ 'Lib Pool SS', 'Lib Pool SS-XP', 'IlluminaHtp::Requests::LibPoolSsToLibPoolSsXp'],
-
[ 'Lib PCR-XP', 'Lib Pool Pippin', 'IlluminaHtp::Requests::PcrXpToPoolPippin' ],
-
[ 'Lib PCRR-XP', 'Lib Pool Pippin', 'IlluminaHtp::Requests::PcrXpToPoolPippin' ],
-
[ 'Lib Pool', 'Lib Pool Norm', 'IlluminaHtp::Requests::LibPoolToLibPoolNorm' ],
-
[ 'Lib Pool SS-XP', 'Lib Pool SS-XP-Norm', 'IlluminaHtp::Requests::LibPoolToLibPoolNorm' ],
-
[ 'Lib PCR-XP', 'Lib Norm', 'IlluminaHtp::Requests::PcrXpToLibNorm' ]
-
]
-
-
1
PLATE_PURPOSE_TYPE = {
-
'PF Cherrypicked' => IlluminaHtp::StockPlatePurpose,
-
'PF Shear' => IlluminaHtp::CovarisPlatePurpose,
-
'PF Post Shear' => PlatePurpose,
-
'PF Post Shear XP' => PlatePurpose,
-
'PF Lib' => PlatePurpose,
-
'PF Lib XP' => PlatePurpose,
-
'PF Lib XP2' => IlluminaHtp::LibraryCompleteOnQcPurpose,
-
'PF EM Pool' => PlatePurpose,
-
'PF Lib Norm' => IlluminaHtp::PooledPlatePurpose,
-
'PF MiSeq Stock' => IlluminaHtp::StockTubePurpose,
-
'PF MiSeq QC' => IlluminaC::QcPoolPurpose,
-
'PF MiSeq QCR' => IlluminaC::QcPoolPurpose,
-
-
-
'Cherrypicked' => IlluminaHtp::StockPlatePurpose,
-
'Shear' => IlluminaHtp::CovarisPlatePurpose,
-
'Post Shear' => PlatePurpose,
-
'AL Libs' => PlatePurpose,
-
'Lib PCR' => PlatePurpose,
-
'Lib PCRR' => PlatePurpose,
-
'Lib PCR-XP' => IlluminaHtp::TransferablePlatePurpose,
-
'Lib PCRR-XP' => IlluminaHtp::TransferablePlatePurpose,
-
'Lib Pool' => IlluminaHtp::InitialStockTubePurpose,
-
'Lib Pool Pippin' => IlluminaHtp::InitialStockTubePurpose,
-
'Lib Pool Norm' => IlluminaHtp::MxTubePurpose,
-
'Lib Pool Conc' => IlluminaHtp::StockTubePurpose,
-
'Lib Pool SS' => IlluminaHtp::StockTubePurpose,
-
'Lib Pool SS-XP' => IlluminaHtp::StockTubePurpose,
-
'Lib Pool SS-XP-Norm' => IlluminaHtp::MxTubePurpose,
-
'Post Shear XP' => PlatePurpose,
-
-
'Post Shear QC' => IlluminaHtp::PostShearQcPlatePurpose,
-
'Lib PCR-XP QC' => PlatePurpose,
-
'Lib PCRR-XP QC' => PlatePurpose,
-
'Lib Norm QC' => PlatePurpose,
-
'PF EM Pool QC' => PlatePurpose,
-
-
'Lib Norm' => IlluminaHtp::InitialDownstreamPlatePurpose,
-
'Lib Norm 2' => IlluminaHtp::NormalizedPlatePurpose,
-
'Lib Norm 2 Pool' => IlluminaHtp::PooledPlatePurpose,
-
-
'Cap Lib Pool Norm' => IlluminaHtp::MxTubeNoQcPurpose
-
-
}
-
-
1
def self.request_type_prefix
-
"Illumina"
-
end
-
-
1
module PurposeHelpers
-
-
-
1
def create_tube_purposes
-
self::TUBE_PURPOSE_FLOWS.each do |flow|
-
create_tube_flow(flow)
-
end
-
end
-
-
1
def create_tube_flow(flow_o)
-
flow = flow_o.clone
-
raise "Flow already exists" if Purpose.find_by_name(flow.first).present?
-
create_tube_purpose(flow.pop, :target_type => 'MultiplexedLibraryTube')
-
flow.each(&method(:create_tube_purpose))
-
end
-
-
1
def destroy_tube_purposes
-
self::TUBE_PURPOSE_FLOWS.each do |flow|
-
Tube::Purpose.find_all_by_name(flow.flatten).map(&:destroy)
-
end
-
end
-
-
1
def create_plate_flow(flow_o)
-
flow = flow_o.clone
-
raise "Flow already exists" if Purpose.find_by_name(flow.first).present?
-
stock_plate = create_plate_purpose(
-
flow.shift,
-
:can_be_considered_a_stock_plate => true,
-
:default_state => 'passed',
-
:cherrypickable_target => true,
-
:cherrypick_filters => [
-
'Cherrypick::Strategy::Filter::ByOverflow',
-
'Cherrypick::Strategy::Filter::ByEmptySpaceUsage',
-
'Cherrypick::Strategy::Filter::BestFit',
-
'Cherrypick::Strategy::Filter::BySpecies',
-
'Cherrypick::Strategy::Filter::InternallyOrderPlexBySubmission'
-
]
-
)
-
-
flow.each do |name|
-
create_plate_purpose(name, :default_location => library_creation_freezer, :source_purpose_id => stock_plate.id)
-
end
-
end
-
-
1
def create_plate_purposes
-
self::PLATE_PURPOSE_FLOWS.each do |flow|
-
create_plate_flow(flow)
-
end
-
create_qc_plates
-
end
-
-
1
def destroy_plate_purposes
-
self::PLATE_PURPOSE_FLOWS.each do |flow|
-
PlatePurpose.find_all_by_name(flow.flatten).map(&:destroy)
-
end
-
end
-
-
1
def create_branch(branch_o)
-
branch = branch_o.clone
-
branch.inject(Purpose.find_by_name!(branch.shift)) do |parent, child|
-
Purpose.find_by_name!(child).tap do |child_purpose|
-
parent.child_relationships.create!(:child => child_purpose, :transfer_request_type => request_type_between(parent, child_purpose))
-
end
-
end
-
end
-
-
1
def create_branches
-
self::BRANCHES.each do |branch|
-
create_branch(branch)
-
end
-
end
-
-
1
def destroy_branches
-
-
end
-
-
1
def purpose_for(name)
-
self::PLATE_PURPOSE_TYPE[name] || raise("NO class configured for #{name}")
-
end
-
1
private :purpose_for
-
-
1
def request_type_between(parent, child)
-
std = RequestPurpose.find_by_key('standard')
-
_, _, request_class = self::PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES.detect { |a,b,_| (parent.name == a) && (child.name == b) }
-
return RequestType.transfer if request_class.nil?
-
request_type_name = "#{request_type_prefix} #{parent.name}-#{child.name}"
-
RequestType.create!(:name => request_type_name, :key => request_type_name.gsub(/\W+/, '_'), :request_class_name => request_class, :asset_type => 'Well', :order => 1,
-
:request_purpose => std
-
)
-
end
-
1
private :request_type_between
-
-
1
def library_creation_freezer
-
Location.find_by_name("Illumina high throughput freezer") or raise "Cannot find Illumina high throughput freezer"
-
end
-
1
private :library_creation_freezer
-
-
1
def create_plate_purpose(plate_purpose_name, options = {})
-
purpose_for(plate_purpose_name).create!(options.reverse_merge(
-
:name => plate_purpose_name,
-
:cherrypickable_target => false,
-
:cherrypick_direction => 'column',
-
:can_be_considered_a_stock_plate => self::OUTPUT_PLATE_PURPOSES.include?(plate_purpose_name),
-
:asset_shape_id => AssetShape.default.id
-
)).tap do |plate_purpose|
-
plate_purpose.barcode_printer_type = BarcodePrinterType.find_by_type('BarcodePrinterType96Plate')||plate_purpose.barcode_printer_type
-
end
-
end
-
1
private :create_plate_purpose
-
-
1
def create_tube_purpose(tube_purpose_name, options = {})
-
purpose = purpose_for(tube_purpose_name)
-
target_type = 'StockMultiplexedLibraryTube'
-
purpose.create!(options.reverse_merge(
-
:name => tube_purpose_name,
-
:target_type => target_type,
-
:barcode_printer_type => BarcodePrinterType.find_by_type('BarcodePrinterType1DTube')
-
))
-
end
-
1
private :create_tube_purpose
-
-
1
def create_qc_plates
-
ActiveRecord::Base.transaction do
-
self::PLATE_PURPOSE_LEADING_TO_QC_PLATES.each do |name|
-
create_qc_plate_for(name)
-
end
-
end
-
end
-
-
1
def create_qc_plate_for(name)
-
qc_plate_purpose = purpose_for("#{name} QC").create!(:name => "#{name} QC", :cherrypickable_target => false)
-
plate_purpose = PlatePurpose.find_by_name(name) or raise StandardError, "Cannot find plate purpose #{name.inspect}"
-
plate_purpose.child_relationships.create!(:child => qc_plate_purpose, :transfer_request_type => RequestType.find_by_name('Transfer'))
-
end
-
end
-
-
1
extend PurposeHelpers
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class IlluminaHtp::PooledPlatePurpose < PlatePurpose
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
ActiveRecord::Base.transaction do
-
super
-
if (state=='passed')
-
plate.wells.with_aliquots.include_stock_wells.find(:all,:select=>'DISTINCT assets.*').each do |well|
-
# As we've already loaded the requests along with the stock wells, the ruby way is about 4 times faster
-
library_creation_request = well.stock_wells.first.requests.detect {|r| r.library_creation? }
-
requests = library_creation_request.submission.obtain_next_requests_to_connect(library_creation_request)
-
requests.reject {|r| r.asset.present? }.slice(0,12).each do |r|
-
r.update_attributes!(:asset => well)
-
end
-
end
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaHtp::PostShearQcPlatePurpose < PlatePurpose
-
1
alias_method(:default_transition_to, :transition_to)
-
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
nudge_parent_plate(plate, state, user, contents)
-
default_transition_to(plate, state, user, contents,customer_accepts_responsibility)
-
end
-
-
1
def nudge_parent_plate(plate, state, contents)
-
case state
-
when 'started' then plate.parent.transition_to('started', user, contents)
-
when 'passed' then plate.parent.transition_to('passed', user, contents)
-
end
-
end
-
1
private :nudge_parent_plate
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
1
module IlluminaHtp::Requests
-
-
1
class StdLibraryRequest < Request::LibraryCreation
-
-
1
fragment_size_details(:no_default, :no_default)
-
-
# Ensure that the bait library information is also included in the pool information.
-
1
def update_pool_information(pool_information)
-
super
-
pool_information[:target_tube_purpose] = target_tube.purpose.uuid if target_tube
-
pool_information[:request_type] = request_type.key
-
end
-
-
1
def role
-
order.role
-
end
-
-
end
-
-
1
class SharedLibraryPrep < StdLibraryRequest
-
1
def target_tube
-
@target_tube ||= submission.next_requests(self).detect {|r| r.target_tube }.try(:target_tube)
-
end
-
-
1
def on_failed
-
submission.next_requests(self).each(&:failed_upstream!)
-
end
-
-
1
validate :valid_purpose?
-
1
def valid_purpose?
-
return true if request_type.acceptable_plate_purposes.include?(asset.plate.purpose)
-
errors.add(:asset,"#{asset.plate.purpose.name} is not a suitable plate purpose.")
-
false
-
end
-
-
1
def failed_downstream!
-
change_decision! unless failed?
-
end
-
-
end
-
-
1
class LibraryCompletion < StdLibraryRequest
-
1
module FailUpstream
-
1
def on_failed
-
asset.requests_as_target.each(&:failed_downstream!)
-
end
-
end
-
1
include FailUpstream
-
end
-
-
1
module InitialDownstream
-
1
def outer_request
-
asset.requests.detect {|request| request.library_creation? && request.submission_id == self.submission_id }
-
end
-
end
-
-
1
class CherrypickedToShear < TransferRequest
-
1
include TransferRequest::InitialTransfer
-
end
-
-
1
class CovarisToSheared < TransferRequest
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :passed
-
1
aasm_state :qc_complete
-
1
aasm_state :failed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started, :failed] end
-
2
aasm_event :qc do transitions :to => :qc_complete, :from => [:passed] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started, :passed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed] end
-
end
-
end
-
-
1
class PostShearToAlLibs < TransferRequest
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :fx_transfer
-
1
aasm_state :failed, :enter => :on_failed
-
1
aasm_state :passed
-
1
aasm_state :cancelled, :enter => :on_cancelled
-
1
aasm_initial_state :pending
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :fx_transfer, :failed] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started, :passed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed] end
-
2
aasm_event :cancel_before_started do transitions :to => :cancelled, :from => [:pending] end
-
2
aasm_event :detach do transitions :to => :pending, :from => [:pending] end
-
2
aasm_event :fx_transfer do transitions :to => :fx_transfer, :from => [:started] end
-
-
end
-
end
-
-
1
class PrePcrToPcr < TransferRequest
-
# This is a legacy state machine.
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started_fx
-
1
aasm_state :started_mj
-
1
aasm_state :passed
-
1
aasm_state :failed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start_fx do transitions :to => :started_fx, :from => [:pending] end
-
2
aasm_event :start_mj do transitions :to => :started_mj, :from => [:started_fx] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started_mj, :failed] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started_fx, :started_mj, :passed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started_fx, :started_mj, :passed] end
-
end
-
end
-
-
1
class PcrXpToPoolPippin < TransferRequest
-
1
include InitialDownstream
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :passed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started, :failed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed, :qc] end
-
end
-
end
-
-
1
class QcCompletableTransfer < TransferRequest
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :passed
-
1
aasm_state :qc_complete
-
1
aasm_state :failed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started, :failed] end
-
2
aasm_event :qc do transitions :to => :qc_complete, :from => [:passed] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started, :passed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed, :qc_complete] end
-
end
-
end
-
-
# We still subclass here both for backwards compatibility, and to make future state machine changes easier
-
1
class PcrXpToStock < QcCompletableTransfer
-
end
-
-
1
class LibPoolSsToLibPoolSsXp < QcCompletableTransfer
-
end
-
-
1
class LibPoolToLibPoolNorm < QcCompletableTransfer
-
end
-
-
1
class PcrToPcrXp < QcCompletableTransfer
-
end
-
-
1
class PcrXpToLibNorm < QcCompletableTransfer
-
end
-
-
1
class PcrXpToPool < PcrXpToStock
-
1
include InitialDownstream
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
-
1
class IlluminaHtp::StockPlatePurpose < PlatePurpose
-
1
include PlatePurpose::Stock
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class IlluminaHtp::StockTubePurpose < Tube::Purpose
-
1
def create_with_request_options(tube)
-
raise 'Unimplemented behaviour'
-
end
-
-
1
def transition_to(tube, state, user, _ = nil, customer_accepts_responsibility = false)
-
tube.requests_as_target.all(not_terminated).each do |request|
-
request.transition_to(state)
-
end
-
outer_requests_for(tube).each do |request|
-
request.customer_accepts_responsibility! if customer_accepts_responsibility
-
request.transition_to(state)
-
end if ['cancelled','failed','aborted'].include?(state)
-
end
-
-
1
def outer_requests_for(tube)
-
tube.requests_as_target.map do |r|
-
r.submission.requests.where_is_a(LibraryCompletion)
-
end.uniq
-
end
-
-
1
def not_terminated
-
{:conditions=>[ 'state NOT IN (?)',['cancelled','failed','aborted']]}
-
end
-
1
private :not_terminated
-
-
1
def pool_id(tube)
-
tube.requests_as_target.first.submission_id
-
end
-
-
1
def name_for_child_tube(tube)
-
tube.name
-
end
-
-
1
def stock_plate(tube)
-
return nil if tube.requests_as_target.empty?
-
-
assets = [ tube.requests_as_target.first.asset ]
-
until assets.empty?
-
asset = assets.shift
-
return asset.plate if asset.is_a?(Well) and asset.plate.stock_plate?
-
assets.push(asset.requests_as_target.first.asset).compact
-
end
-
-
raise "Cannot locate stock plate for #{tube.display_name.inspect}"
-
end
-
-
1
def stock_wells(tube)
-
tube.requests_as_target.map do |request|
-
request.asset.stock_wells
-
end.flatten
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
1
class IlluminaHtp::TransferablePlatePurpose < IlluminaHtp::FinalPlatePurpose
-
1
include PlatePurpose::Library
-
1
include PlatePurpose::RequestAttachment
-
1
include PlatePurpose::WorksOnLibraryRequests
-
-
1
self.connect_on = 'qc_complete'
-
1
self.connect_downstream = true
-
1
self.connected_class = IlluminaHtp::Requests::SharedLibraryPrep
-
-
1
def source_wells_for(wells)
-
Well.in_column_major_order.stock_wells_for(wells)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Implement < ActiveRecord::Base
-
1
validates_presence_of :name
-
1
validates :barcode, :presence => true, :on => :update
-
1
@@barcode_prefix = "LE"
-
-
1
def generate_barcode
-
raise Exception.new , "Can't generate barcode with a null ID" if id == 0
-
b = Barcode.calculate_barcode(barcode_prefix, id)
-
Barcode.barcode_to_human b
-
end
-
-
1
def barcode_prefix
-
@@barcode_prefix
-
end
-
-
1
def human_barcode
-
Barcode.barcode_to_human self.barcode
-
end
-
-
1
def save_and_generate_barcode
-
ActiveRecord::Base.transaction do
-
save and self.barcode = generate_barcode
-
save
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class Item < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include EventfulRecord
-
1
include Workflowed
-
1
extend EventfulRecord
-
1
has_many_events
-
1
has_many_lab_events
-
-
1
@@cached_requests = nil
-
-
1
belongs_to :submission
-
1
belongs_to :study
-
-
1
has_many :requests, :dependent => :destroy
-
1
has_many :comments, :as => :commentable
-
-
1
validates_presence_of :version
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :scope => [:workflow_id, :version], :on => :create, :message => "already in use (item)"
-
-
1
scope :for_search_query, ->(query,with_includes) {
-
where([ 'name LIKE ? OR id=?', "%#{query}%", query ])
-
}
-
-
1
before_validation :set_version, :on => :create
-
-
1
def set_version
-
things_with_same_name = self.class.all(:conditions => {:name => self.name, :workflow_id => self.workflow_id})
-
if things_with_same_name.empty?
-
self.increment(:version)
-
else
-
self.version = things_with_same_name.size + 1
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2015 Genome Research Ltd.
-
1
class LabEvent < ActiveRecord::Base
-
1
belongs_to :batch
-
1
belongs_to :user
-
1
belongs_to :eventful, :polymorphic => true
-
1
acts_as_descriptable :serialized
-
-
1
before_validation :unescape_for_descriptors
-
-
1
scope :with_descriptor, ->(k,v) { { :conditions => [ 'descriptors LIKE ?', "%#{k.to_s}: #{v.to_s}%" ] } }
-
-
1
scope :barcode_code, ->(*args) { {:conditions => ["(description = 'Cluster generation' or description = 'Add flowcell chip barcode') and eventful_type = 'Request' and descriptors like ? ", args[0]] }}
-
-
-
1
def unescape_for_descriptors
-
self[:descriptors] = (self[:descriptors] || {}).inject({}) do |hash,(key,value)|
-
hash[ CGI.unescape(key) ] = value
-
hash
-
end
-
end
-
-
1
def self.find_by_barcode(barcode)
-
batch_id = 0
-
-
search = "%Chip Barcode: " + barcode +"%"
-
requests = self.barcode_code(search)
-
batch = requests.map(&:batch_id).uniq
-
batch_id = batch[0] unless batch.size != 1
-
-
return batch_id
-
end
-
-
1
def descriptor_value_for(name)
-
self.descriptors.each do |desc|
-
if desc.name.downcase == name.to_s.downcase
-
return desc.value
-
end
-
end
-
return nil
-
end
-
-
1
def add_new_descriptor(name, value)
-
add_descriptor Descriptor.new(:name => name, :value => value)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class LabInterface < ActiveRecord::Base
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014 Genome Research Ltd.
-
1
class LabInterface::Workflow < ActiveRecord::Base
-
-
1
has_many :tasks, :order => 'sorted', :dependent => :destroy, :foreign_key => :pipeline_workflow_id
-
1
has_many :families
-
-
1
belongs_to :pipeline
-
1
validates_uniqueness_of :pipeline_id, :message => 'only one workflow per pipeline!'
-
-
1
validates_uniqueness_of :name
-
-
1
def has_batch_limit?
-
!self.item_limit.blank?
-
end
-
-
1
def controls
-
self.families
-
end
-
-
1
def source_is_external?
-
self.locale == 'External' ? true : false
-
end
-
-
1
def source_is_internal?
-
self.locale == 'Internal' ? true : false
-
end
-
-
1
def assets
-
collection = []
-
self.tasks.each do |task|
-
task.families.each do |family|
-
collection.push family
-
end
-
end
-
collection
-
end
-
-
1
def deep_copy(suffix="_dup", skip_pipeline=false)
-
self.dup.tap do |new_workflow|
-
ActiveRecord::Base.transaction do
-
new_workflow.name = new_workflow.name + suffix
-
new_workflow.tasks = tasks.map do |task|
-
new_task = task.dup
-
new_task.descriptors = task.descriptors.map do |descriptor|
-
Descriptor.create descriptor.attributes
-
end
-
new_task
-
end
-
new_workflow.pipeline = nil
-
new_workflow.save!
-
-
#copy of the pipeline
-
unless skip_pipeline
-
new_workflow.build_pipeline(self.pipeline.attributes.merge(:workflow => new_workflow))
-
new_workflow.pipeline.request_types = self.pipeline.request_types
-
new_workflow.pipeline.name += suffix
-
new_workflow.pipeline.save!
-
end
-
end
-
end
-
end
-
-
1
def change_sorter_of_all_tasks(value)
-
return nil if self.tasks.nil?
-
self.tasks.each do |task|
-
next if task.sorted+value <0
-
task.sorted = task.sorted+value
-
task.save
-
end
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# A simple class to handle the behaviour from the labwhere reception controller
-
1
class LabwhereReception
-
# The following two modules include methods used by a number of rails
-
# helpers, such that we can use them in eg. form_for
-
1
extend ActiveModel::Naming
-
1
include ActiveModel::Conversion
-
-
1
attr_reader :errors, :asset_barcodes, :user_code, :location_barcode, :location_id
-
-
1
def initialize(user_code,location_barcode,location_id,asset_barcodes)
-
@asset_barcodes = asset_barcodes.map(&:strip)
-
@location_id = location_id.to_i
-
@location_barcode = location_barcode.try(:strip)
-
@user_code = user_code.try(:strip)
-
@errors = []
-
end
-
-
1
def location
-
@location ||= Location.find_by_id(location_id)
-
end
-
-
1
def id; nil; end
-
1
def persisted?; false; end
-
1
def new_record?; true; end
-
-
# save attempts to perform the actions, and returns true if it was successful
-
# This maintains compatibility with rails
-
1
def save
-
return false unless valid?
-
-
begin
-
scan = LabWhereClient::Scan.create(
-
:location_barcode=> location_barcode,
-
:user_code => user_code,
-
:labware_barcodes => asset_barcodes
-
)
-
-
return add_error(scan.error) unless scan.valid?
-
rescue LabWhereClient::LabwhereException => exception
-
add_error("Could not connect to Labwhere. Sequencescape location has still been updated")
-
end
-
-
assets.each do |asset|
-
asset.location = location
-
asset.events.create_scanned_into_lab!(location)
-
end
-
-
@valid
-
end
-
-
1
private
-
-
1
def valid?
-
@valid = true
-
add_error('Could not find specified location in Sequencescape') if location.nil?
-
add_error("Could not find labware #{missing_assets.join(', ')} in Sequencescape") unless missing_assets.empty?
-
add_error("No user supplied") if user_code.blank?
-
@valid
-
end
-
-
1
def add_error(message)
-
errors << message
-
@valid = false
-
end
-
-
1
def assets
-
@assets ||= Asset.with_machine_barcode(asset_barcodes)
-
end
-
-
1
def missing_assets
-
add_error('No barcodes scanned in!') if asset_barcodes.empty?
-
asset_barcodes - assets.map(&:machine_barcode)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class Lane < Aliquot::Receptacle
-
1
include Api::LaneIO::Extensions
-
1
include LocationAssociation::Locatable
-
1
include AliquotIndexer::Indexable
-
-
1
def subject_type
-
'lane'
-
end
-
-
1
LIST_REASONS_NEGATIVE = [
-
"Failed on yield but sufficient data for experiment",
-
"Failed on quality but sufficient data for experiment",
-
"Failed on adapter contamination but data sufficient for experiment"
-
-
]
-
-
1
LIST_REASONS_POSITIVE = [
-
"Data doesn't contain any of the expected organism",
-
"Data doesn't reflect the experiment",
-
"GC bias in data set",
-
"Multiplex tag problems in data set",
-
"Unsure data source"
-
]
-
-
1
LIST_REASONS = [""] + LIST_REASONS_NEGATIVE + LIST_REASONS_POSITIVE
-
-
1
SAMPLE_PARTIAL = 'assets/samples_partials/lane_samples'
-
-
1
extend Metadata
-
1
has_metadata do
-
1
attribute(:release_reason, :in => LIST_REASONS)
-
end
-
-
1
has_one_as_child(:spiked_in_buffer, :conditions => { :sti_type => 'SpikedBuffer' })
-
-
1
has_many :aliquot_indicies, :inverse_of => :lane, :class_name => 'AliquotIndex'
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class LibraryCreationPipeline < Pipeline
-
1
def library_creation?
-
true
-
end
-
-
1
def pulldown?
-
false
-
end
-
-
1
def update_detached_request(batch, request)
-
super
-
batch.remove_link(request)
-
end
-
-
# This is specific for multiplexing batches for plates
-
# Is this still used?
-
1
def create_batch_from_assets(assets)
-
batch = create_batch
-
ActiveRecord::Base.transaction do
-
assets.each do |asset|
-
parent_asset_with_request = asset.parents.select{|parent| ! parent.requests.empty? }.first
-
request = parent_asset_with_request.requests.find_by_state_and_request_type_id("pending", self.request_type_id)
-
request.create_batch_request!(:batch => batch, :position => asset.map.location_id)
-
request.update_attributes!(:target_asset => asset)
-
request.start!
-
end
-
end
-
batch
-
end
-
-
1
def can_create_stock_assets?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class LibraryCreationRequest < CustomerRequest
-
-
# NOTE: Do not alter the order here:
-
#
-
# 1. has_metadata :as => Request
-
# 2. include Request::LibraryManufacture
-
# 3. class RequestOptionsValidator
-
#
-
# These are dependent upon each other
-
1
has_metadata :as => Request do
-
# /!\ We don't check the read_length, because we don't know the restriction, that depends on the SequencingRequest
-
1
attribute(:read_length, :integer => true) # meaning , so not required but some people want to set it
-
1
attribute(:gigabases_expected, :positive_float => true)
-
end
-
-
1
include Request::CustomerResponsibility
-
1
include Request::LibraryManufacture
-
-
# When a library creation request passes it does the default behaviour of a request but also adds the
-
# insert size to the aliquots in the target asset and sets the library. There's a minor complication in that
-
# an MX library is also a type of library that might have libraries coming into it, therefore we only update the
-
# information that is missing.
-
1
def on_started
-
ActiveRecord::Base.transaction do
-
super
-
target_asset.aliquots.each do |aliquot|
-
aliquot.library ||= target_asset
-
aliquot.library_type ||= library_type
-
aliquot.insert_size ||= insert_size
-
aliquot.save!
-
end
-
end
-
end
-
-
1
def request_options_for_creation
-
Hash[[:fragment_size_required_from, :fragment_size_required_to, :library_type].map { |f| [ f, request_metadata[f] ] }]
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class LibraryTube < Tube
-
1
include Api::LibraryTubeIO::Extensions
-
1
include ModelExtensions::LibraryTube
-
-
1
def is_sequenceable?
-
true
-
end
-
-
1
def library_prep?
-
true
-
end
-
-
1
def can_be_created?
-
true
-
end
-
-
1
scope :include_tag, -> { includes( :aliquots => { :tag => [ :uuid_object, { :tag_group => :uuid_object } ] } ) }
-
-
1
def sorted_tags_for_select
-
self.get_tag.tag_group.tags.sort{ |a,b| a.map_id <=> b.map_id }.collect { |t| [t.name, t.id] }
-
end
-
-
# A library tube is created with request options that come from the request in which it is the target asset.
-
1
def created_with_request_options
-
creation_request.try(:request_options_for_creation) || {}
-
end
-
-
1
def self.stock_asset_type
-
StockLibraryTube
-
1
end
-
-
1
def self.stock_asset_purpose
-
Tube::Purpose.stock_library_tube
-
end
-
-
1
def specialized_from_manifest=(attributes)
-
aliquots.first.update_attributes!(attributes.merge(:library_id => self.id))
-
requests.map(&:manifest_processed!)
-
end
-
-
1
def library_information
-
tag = aliquots.first.tag
-
tag2 = aliquots.first.tag2
-
{
-
:library_type => aliquots.first.library_type,
-
:insert_size_from => aliquots.first.insert_size_from,
-
:insert_size_to => aliquots.first.insert_size_to
-
}.tap do |tag_hash|
-
tag_hash.merge!(:tag=>tag.summary) if tag
-
tag_hash.merge!(:tag2=>tag2.summary) if tag2
-
end
-
end
-
-
1
def library_information=(library_information)
-
-
library_information[:tag] = find_tag(library_information[:tag])
-
library_information[:tag2] = find_tag(library_information[:tag2]) if library_information[:tag2]
-
-
self.specialized_from_manifest= library_information
-
end
-
-
1
def library_source_plates
-
purpose.try(:library_source_plates,self)||[]
-
end
-
-
1
def find_tag(tag_info)
-
tag_group = Uuid.with_resource_type('TagGroup').include_resource.find_by_external_id!(tag_info['tag_group']).resource
-
tag_group.tags.find_by_map_id!(tag_info['tag_index'])
-
end
-
1
private :find_tag
-
-
1
extend Asset::Stock::CanCreateStockAsset
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class LibraryType < ActiveRecord::Base
-
-
1
validates_presence_of :name
-
-
1
has_many :library_types_request_types, :inverse_of=> :library_type
-
1
has_many :request_types, :through => :library_types_request_types
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class LibraryTypesRequestType < ActiveRecord::Base
-
-
1
belongs_to :library_type, :inverse_of => :library_types_request_types
-
1
belongs_to :request_type, :inverse_of => :library_types_request_types
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
# this class it simplified version of Submission which is just a chain of request types
-
# without any choices
-
1
class LinearSubmission < Order
-
1
include Submission::LinearRequestGraph
-
-
1
def request_type_ids=(id_list)
-
self.request_type_ids_list = id_list.map {|i| [i] }
-
end
-
-
1
def request_type_ids
-
request_type_ids_list.map(&:first)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Location < ActiveRecord::Base
-
1
has_many :pipelines
-
#has_many :assets, :as => :holder
-
-
1
def set_locations(assets)
-
assets.each do |asset|
-
asset.location = self
-
asset.save
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013 Genome Research Ltd.
-
class LocationAssociation < ActiveRecord::Base
-
belongs_to :locatable, :class_name => "Asset"
-
belongs_to :location
-
-
validates_uniqueness_of :locatable_id
-
validates :location_id, :locatable_id, :presence => true
-
-
after_save :update_locatable
-
-
def update_locatable
-
locatable.touch
-
end
-
-
module Locatable
-
def self.included(base)
-
base.class_eval do
-
has_one :location_association, :foreign_key => :locatable_id, :inverse_of => :locatable
-
1
-
has_one :location, :through => :location_association
-
delegate :location_id, :to => :location_association, :allow_nil => true
-
-
1
scope :located_in, ->(location) { {
-
:joins => 'INNER JOIN `location_associations` ON `assets`.id=`location_associations`.`locatable_id`',
-
:conditions => [ '`location_associations`.`location_id`=?', location.id ]
-
} }
-
-
# TODO: not optimal
-
1
def location_id=(l_id)
-
location = l_id && Location.find(l_id)
-
self.location = location
-
end
-
-
end
-
1
base.extend(ClassMethods)
-
end
-
-
module ClassMethods
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
##
-
# A lot represents a received batch of consumables (eg. tag plates)
-
# that can be assumed to share some level of QC.
-
-
class Lot < ActiveRecord::Base
-
-
module Template
-
def self.included(base)
-
3
base.class_eval do
-
3
belongs_to :lot
-
end
-
end
-
end
-
-
# include Api::LotIO::Extensions
-
include Uuid::Uuidable
-
-
belongs_to :lot_type
-
belongs_to :user
-
belongs_to :template, :polymorphic => true
-
-
has_many :qcables, :inverse_of => :lot
-
-
has_many :stamps, :inverse_of => :lot
-
-
validates :lot_number, :lot_type, :user, :template, :received_at, :presence => true
-
validates_uniqueness_of :lot_number
-
-
validate :valid_template?
-
1
-
delegate :valid_template_class, :target_purpose, :to => :lot_type
-
-
1
scope :include_lot_type, -> { includes(:lot_type) }
-
1
scope :include_template, -> { includes(:template) }
-
1
scope :with_lot_number, ->(lot_number) { {:conditions=>{:lot_number=>lot_number} } }
-
-
1
scope :with_qc_asset, ->(qc_asset) {
-
return { :conditions => 'FALSE' } if qc_asset.nil?
-
sibling = qc_asset.transfers_as_destination.first.source
-
-
includes(:qcables).where(['qcables.asset_id IN(?) AND qcables.state != ?',[qc_asset.id,sibling.id],'exhausted' ])
-
}
-
-
1
private
-
-
1
def valid_template?
-
return false unless lot_type.present?
-
return true if template.is_a?(valid_template_class)
-
errors.add(:template,"is not an appropriate type for this lot. Received #{template.class} expected #{valid_template_class}.")
-
false
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
##
-
# A lot type governs the behaviour of a lot
-
-
1
class LotType < ActiveRecord::Base
-
-
1
include Uuid::Uuidable
-
-
1
has_many :lots, :inverse_of => :lot_type
-
1
belongs_to :target_purpose, :class_name => 'Purpose'
-
-
1
validates :name, :template_class, :presence => true
-
1
validates_uniqueness_of :name
-
-
1
def valid_template_class
-
template_class.constantize
-
end
-
-
1
def create!(options)
-
self.lots.create!(options)
-
end
-
-
1
def printer_type
-
target_purpose.barcode_printer_type.name
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
class ManifestGenerator
-
-
1
QUOTE_CHAR = "!"
-
1
DEFAULT_VOLUME = 13
-
1
DEFAULT_CONCENTRATION = 50
-
1
DEFAULT_SPECIES = "Homo sapiens"
-
1
DEFAULT_IS_CONTROL = 0
-
-
1
def self.generate_manifests(batch,study)
-
generate_manifest_for_plate_ids(batch.plate_ids_in_study(study),study)
-
end
-
-
1
def self.generate_manifest_for_plate_ids(plate_ids,study)
-
csv_string = CSV.generate(:row_sep => "\n", :quote_char => "#{QUOTE_CHAR}") do |csv|
-
create_header(csv,study)
-
row = 1
-
plate_ids.each do |plate_id|
-
plate = Plate.find(plate_id)
-
plate_label = institute_plate_label(plate)
-
plate.wells_sorted_by(&:id).each do |well|
-
csv << self.generate_manifest_row(well,plate.barcode,plate_label).unshift(row)
-
row = row +1
-
end
-
end
-
end
-
csv_string = remove_empty_quotes(csv_string)
-
-
csv_string
-
end
-
-
1
def self.generate_manifest_row(well,plate_barcode,plate_label)
-
comments = ""
-
extraction_method = "-"
-
wga_method = ""
-
dna_mass = 0
-
replicates = ""
-
tissue_source = "-"
-
-
[ "#{plate_label}",
-
well_map_description(well),
-
well_sample_is_control(well),
-
construct_sample_label(plate_barcode, well),
-
well_sample_species(well),
-
well_sample_gender(well),
-
"#{comments}",
-
well_volume(well),
-
well_concentration(well),
-
"#{extraction_method}",
-
"#{wga_method}",
-
"#{dna_mass}",
-
well_sample_parent(well, 'mother'),
-
well_sample_parent(well, 'father'),
-
"#{replicates}",
-
"#{tissue_source}"
-
]
-
end
-
-
1
private
-
1
def self.check_well_sample_exists(well)
-
raise StandardError, "Sample not found for well #{well.id}" if well.primary_aliquot.nil?
-
end
-
-
1
def self.institute_plate_label(plate)
-
plate.infinium_barcode
-
end
-
-
1
def self.well_map_description(well)
-
description = well.map_description
-
if description && description.size == 2
-
return "#{description[0].chr}0#{description[1].chr}"
-
end
-
-
description || ""
-
end
-
-
1
def self.well_sample_parent(well, parent)
-
check_well_sample_exists(well)
-
well.primary_aliquot.sample.sample_metadata[parent].try(:to_i)
-
end
-
-
1
def self.well_sample_gender(well)
-
check_well_sample_exists(well)
-
case gender = well.primary_aliquot.sample.sample_metadata.gender.try(:downcase)
-
when 'male' then 'M'
-
when 'female' then 'F'
-
when 'not applicable' then 'U'
-
when 'mixed' then 'U'
-
when 'hermaphrodite' then 'U'
-
when 'unknown' then 'U'
-
when nil then 'U'
-
else raise StandardError, "Unknown gender type #{gender.inspect}"
-
end
-
end
-
-
1
def self.well_sample_is_control(well)
-
check_well_sample_exists(well)
-
control_value = well.primary_aliquot.sample.try(:control)
-
if control_value == true
-
1
-
elsif control_value == false
-
0
-
else
-
DEFAULT_IS_CONTROL
-
end
-
end
-
-
1
def self.well_sample_species(well)
-
check_well_sample_exists(well)
-
well.primary_aliquot.sample.sample_metadata.sample_common_name || DEFAULT_SPECIES
-
end
-
-
1
def self.well_volume(well)
-
volume = well.get_requested_volume.to_i
-
volume = DEFAULT_VOLUME if volume == 0 || volume.nil?
-
-
volume
-
end
-
-
1
def self.well_concentration(well)
-
concentration = well.get_concentration.to_i
-
concentration = DEFAULT_CONCENTRATION if concentration == 0 || concentration.nil?
-
-
concentration
-
end
-
-
1
def self.construct_sample_label(plate_barcode, well)
-
check_well_sample_exists(well)
-
plate_barcode + "_" + well_map_description(well) + "_" + well.primary_aliquot.sample.sanger_sample_id
-
end
-
-
1
def self.create_header(csv_obj,study)
-
now = Time.new
-
csv_obj << ["Institute Name:", "WTSI","","","","","","","","","","","","","","","",""]
-
csv_obj << ["Date:", "#{now.year}-#{now.month}-#{now.day}"]
-
csv_obj << ["Comments:", "#{study.abbreviation}"]
-
csv_obj << ["Row", "Institute Plate Label", "Well", "Is Control", "Institute Sample Label", "Species", "Sex", "Comments", "Volume (ul)", "Conc (ng/ul)", "Extraction Method", "WGA Method (if Applicable)", "Mass of DNA used in WGA", "Parent 1", "Parent 2", "Replicate(s)", "Tissue Source"]
-
end
-
-
1
def self.remove_empty_quotes(csv_string)
-
csv_string.gsub!("#{QUOTE_CHAR}#{QUOTE_CHAR}", "")
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015 Genome Research Ltd.
-
class Map < ActiveRecord::Base
-
-
validates_presence_of :description, :asset_size, :location_id, :row_order, :column_order, :asset_shape
-
validates_numericality_of :asset_size, :row_order, :column_order
-
-
module Coordinate
-
-
# TODO: These methods are only valid for standard plates. Moved them here to make that more explicit
-
# (even if its not strictly appropriate) They could do with refactoring/removing.
-
-
PLATE_DIMENSIONS = Hash.new { |h,k| [] }.merge(
-
96 => [ 12, 8 ],
-
384 => [ 24, 16 ]
-
)
-
-
def self.location_from_row_and_column(row, column,_=nil,__=nil)
-
"#{(?A.getbyte(0)+row).chr}#{column}"
-
end
-
-
def self.description_to_horizontal_plate_position(well_description,plate_size)
-
return nil unless Map.valid_well_description_and_plate_size?(well_description,plate_size)
-
split_well = Map.split_well_description(well_description)
-
width = self.plate_width(plate_size)
-
return nil if width.nil?
-
(width*split_well[:row]) + split_well[:col]
-
end
-
-
def self.description_to_vertical_plate_position(well_description,plate_size)
-
return nil unless Map.valid_well_description_and_plate_size?(well_description,plate_size)
-
split_well = Map.split_well_description(well_description)
-
length = self.plate_length(plate_size)
-
return nil if length.nil?
-
(length*(split_well[:col]-1)) + split_well[:row]+1
-
end
-
-
def self.horizontal_plate_position_to_description(well_position,plate_size)
-
return nil unless Map.valid_plate_position_and_plate_size?(well_position,plate_size)
-
width = plate_width(plate_size)
-
return nil if width.nil?
-
horizontal_position_to_description(well_position, width)
-
end
-
-
def self.vertical_plate_position_to_description(well_position,plate_size)
-
return nil unless Map.valid_plate_position_and_plate_size?(well_position,plate_size)
-
length = plate_length(plate_size)
-
return nil if length.nil?
-
vertical_position_to_description(well_position, length)
-
end
-
-
def self.descriptions_for_row(row,size)
-
(1..plate_width(size)).map {|column| "#{row}#{column}"}
-
end
-
-
def self.descriptions_for_column(column,size)
-
(0...plate_length(size)).map {|row| Map.location_from_row_and_column(row,column)}
-
end
-
-
def self.plate_width(plate_size)
-
PLATE_DIMENSIONS[plate_size].first
-
end
-
-
def self.plate_length(plate_size)
-
PLATE_DIMENSIONS[plate_size].last
-
end
-
-
def self.vertical_position_to_description(well_position, length)
-
desc_letter = (((well_position-1)%length) + 65).chr
-
desc_number = ((well_position-1)/length) +1
-
(desc_letter+(desc_number.to_s))
-
end
-
-
def self.horizontal_position_to_description(well_position, width)
-
desc_letter = (((well_position-1)/width) + 65).chr
-
desc_number = ((well_position-1)%width) +1
-
(desc_letter+(desc_number.to_s))
-
end
-
-
def self.horizontal_to_vertical(well_position,plate_size)
-
alternate_position(well_position, plate_size, :width, :length)
-
end
-
-
def self.vertical_to_horizontal(well_position,plate_size)
-
alternate_position(well_position, plate_size, :length, :width)
-
end
-
-
def self.location_from_index(index, size)
-
horizontal_plate_position_to_description(index-1,size)
-
end
-
-
class << self
-
# Given the well position described in terms of a direction (vertical or horizontal) this function
-
# will map it to the alternate positional representation, i.e. a vertical position will be mapped
-
# to a horizontal one. It does this with the divisor and multiplier, which will be reversed for
-
# the alternate.
-
#
-
# NOTE: I don't like this, it just makes things clearer than it was!
-
# NOTE: I hate the nil returns but external code would take too long to change this time round
-
def alternate_position(well_position, size, *dimensions)
-
return nil unless Map.valid_well_position?(well_position)
-
divisor, multiplier = dimensions.map { |n| send("plate_#{n}", size) }
-
return nil if divisor.nil? or multiplier.nil?
-
column, row = (well_position-1).divmod(divisor)
-
return nil unless (0...multiplier).include?(column)
-
return nil unless (0...divisor).include?(row)
-
alternate = (row * multiplier) + column + 1
-
end
-
private :alternate_position
-
end
-
-
end
-
-
module Sequential
-
-
def self.location_from_row_and_column(row, column, width, size)
-
digit_count = Math.log10(size+1).ceil
-
"S%0#{digit_count}d" % [(row)*width + column]
-
end
-
-
def self.location_from_index(index, size)
-
digit_count = Math.log10(size+1).ceil
-
"S%0#{digit_count}d" % [index+1]
-
end
-
-
end
-
-
scope :for_position_on_plate, ->(position,plate_size,asset_shape) {
-
{
-
:conditions => {
-
:row_order => position - 1,
-
:asset_size => plate_size,
-
:asset_shape_id => asset_shape.id
-
}
-
}
-
}
-
-
scope :where_description, ->(*descriptions) { where(:description => descriptions.flatten) }
-
scope :where_plate_size, ->(size) { where(:asset_size => size) }
-
scope :where_plate_shape, ->(asset_shape) { where(:asset_shape_id => asset_shape)}
-
scope :where_vertical_plate_position, ->(*positions) { where(:column_order => positions.map {|v| v-1}) }
-
-
1
belongs_to :asset_shape, :class_name => 'AssetShape'
-
delegate :standard?, :to => :asset_shape
-
-
1
def self.valid_plate_size?(plate_size)
-
plate_size.is_a?(Integer) && plate_size >0
-
end
-
-
1
def self.valid_plate_position_and_plate_size?(well_position,plate_size)
-
return false unless valid_well_position?(well_position)
-
return false unless valid_plate_size?(plate_size)
-
return false if well_position > plate_size
-
true
-
end
-
-
1
def self.valid_well_description_and_plate_size?(well_description,plate_size)
-
return false if well_description.blank?
-
return false unless valid_plate_size?(plate_size)
-
true
-
end
-
-
1
def self.valid_well_position?(well_position)
-
well_position.is_a?(Integer) && well_position >0
-
end
-
-
1
def vertical_plate_position
-
self.column_order + 1
-
end
-
-
1
def height
-
asset_shape.plate_height(asset_size)
-
end
-
-
1
def width
-
asset_shape.plate_width(asset_size)
-
end
-
-
##
-
# Column of particular map location. Zero indexed integer
-
1
def column
-
self.row_order%width
-
end
-
-
##
-
# Row of particular map location. Zero indexed integer
-
1
def row
-
self.column_order%height
-
end
-
-
1
def horizontal_plate_position
-
self.row_order + 1
-
end
-
-
1
def snp_id
-
raise StandardError, "Only standard maps can be converted to SNP" unless map.standard?
-
self.horizontal_plate_position
-
end
-
-
1
def self.location_from_row_and_column(row, column)
-
"#{('A'.getbyte(0)+row).chr}#{column}"
-
end
-
-
1
def self.next_map_position(current_map_id)
-
Map.find(current_map_id).next_map_position
-
end
-
-
1
def next_map_position
-
Map.find(:first,:conditions=>{
-
:asset_size=> asset_size,
-
:asset_shape_id=> asset_shape_id,
-
:row_order => row_order+1
-
})
-
end
-
-
1
def self.horizontal_to_vertical(well_position,plate_size,plate_shape=nil)
-
Map::Coordinate.horizontal_to_vertical(well_position,plate_size)
-
end
-
-
1
def self.vertical_to_horizontal(well_position,plate_size,plate_shape=nil)
-
Map::Coordinate.vertical_to_horizontal(well_position,plate_size)
-
end
-
-
1
def self.next_vertical_map_position(current_map_id)
-
Map.find(current_map_id).next_vertical_map_position
-
end
-
-
1
def next_vertical_map_position
-
Map.find(:first,:conditions=>{
-
:asset_size=> asset_size,
-
:asset_shape_id=> asset_shape_id,
-
:column_order => column_order+1
-
})
-
end
-
-
1
def self.map_96wells
-
Map.all(:conditions => {:asset_size => 96})
-
end
-
-
1
def self.map_384wells
-
Map.all(:conditions => {:asset_size => 384})
-
end
-
-
1
def self.snp_map_id_to_pipelines_map_id(snp_map_id,plate_size)
-
# We're only going to be getting standard plates in through SNP
-
Map.find(:first,:conditions=>{
-
:asset_size => plate_size,
-
:row_order => snp_map_id.to_i+1,
-
:asset_shape => AssetShape.default_id
-
}).id
-
end
-
-
1
def self.pipelines_map_id_to_snp_map_id(pipelines_map_id)
-
# We're only going to be getting standard plates in through SNP
-
Map.find(pipelines_map_id).snp_id
-
end
-
-
1
def self.split_well_description(well_description)
-
{ :row=> well_description.getbyte(0) - 65, :col=> well_description[1,well_description.size].to_i}
-
end
-
-
1
def self.find_for_cell_location(cell_location, asset_size)
-
self.find_by_description_and_asset_size(cell_location.sub(/0(\d)$/, '\1'), asset_size)
-
end
-
-
1
def self.pad_description(map)
-
split_description = split_well_description(map.description)
-
return "#{map.description[0].chr}0#{split_description[:col]}" if split_description[:col] < 10
-
-
map.description
-
end
-
-
1
scope :in_row_major_order, -> { order('row_order ASC' ) }
-
1
scope :in_reverse_row_major_order, -> { order('row_order DESC' ) }
-
1
scope :in_column_major_order, -> { order('column_order ASC' ) }
-
1
scope :in_reverse_column_major_order, -> { order('column_order DESC' ) }
-
-
1
class << self
-
# Caution! Only use for seeds. Not valid elsewhere
-
1
def plate_dimensions(plate_size)
-
case plate_size
-
when 96 then yield(12, 8)
-
when 384 then yield(24, 16)
-
else raise StandardError, "Cannot determine plate dimensions for #{plate_size}"
-
end
-
end
-
-
# Walking in column major order goes by the columns: A1, B1, C1, ... A2, B2, ...
-
1
def walk_plate_in_column_major_order(size, asset_shape=nil, &block)
-
asset_shape ||= AssetShape.default_id
-
self.all(:conditions => { :asset_size => size, :asset_shape_id => asset_shape }, :order => 'column_order ASC').each do |position|
-
yield(position, position.column_order)
-
end
-
end
-
1
alias_method(:walk_plate_vertically, :walk_plate_in_column_major_order)
-
-
# Walking in row major order goes by the rows: A1, A2, A3, ... B1, B2, B3 ....
-
1
def walk_plate_in_row_major_order(size, asset_shape=nil, &block)
-
asset_shape ||= AssetShape.default_id
-
self.all(:conditions => { :asset_size => size, :asset_shape_id => asset_shape }, :order => 'row_order ASC').each do |position|
-
yield(position, position.row_order)
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
class Messenger < ActiveRecord::Base
-
-
1
belongs_to :target, :polymorphic => true
-
1
validates_presence_of :target, :root, :template
-
-
1
def shoot
-
raise StandardErrror, "Hey, don't shoot the messenger"
-
end
-
-
1
def render_class
-
"Api::Messages::#{template}".constantize
-
end
-
-
1
def routing_key
-
"#{Rails.env}.message.#{root}.#{id}"
-
end
-
-
1
def as_json(options = {})
-
{ root => render_class.to_hash(target),
-
'lims' => configatron.amqp.lims_id! }
-
end
-
-
1
def resend
-
AmqpObserver.instance << self
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
##
-
# A messenger creator acts as a message factory for a given
-
# for a given plate. They are currently triggered by:
-
# 1. Cherrypick batch release
-
# They specify both a template (under Api::Messages) and a root
-
1
class MessengerCreator < ActiveRecord::Base
-
-
1
belongs_to :purpose
-
1
validates_presence_of :purpose, :root, :template
-
-
1
validate :template_exists?
-
-
1
def create!(target)
-
Messenger.create!(:target=>target, :root=>root, :template=>template)
-
end
-
-
1
private
-
-
1
def template_exists?
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014 Genome Research Ltd.
-
require 'app/models/attributable'
-
-
module Metadata
-
def has_metadata(options = {}, &block)
-
as_class = options.delete(:as) || self
-
table_name = options.delete(:table_name) ||"#{ as_class.name.demodulize.underscore }_metadata"
-
construct_metadata_class(table_name, as_class, &block)
-
build_association(as_class, options)
-
end
-
-
SECTION_FIELDS = [ :edit_info, :help, :label, :unspecified ]
-
Section = Struct.new(*SECTION_FIELDS,:label_options)
-
-
private
-
-
def build_association(as_class, options)
-
# First we build the association into the current ActiveRecord::Base class
-
as_name = as_class.name.demodulize.underscore
-
association_name = "#{ as_name }_metadata".underscore.to_sym
-
class_name = "#{ self.name}::Metadata"
-
-
has_one(association_name, { :class_name => class_name, :dependent => :destroy, :validate => true, :autosave => true, :inverse_of => :owner }.merge(options).merge(:foreign_key => "#{as_name}_id", :inverse_of => :owner))
-
accepts_nested_attributes_for(association_name, :update_only => true)
-
scope :"include_#{ association_name }", -> { includes(association_name) }
-
-
# We now ensure that, if the metadata is not already created, that a blank instance is built. We cannot
-
# do this through the initialization of our model because we use the ActiveRecord::Base#becomes method in
-
# our code, which would create a new default instance.
-
line = __LINE__ + 1
-
class_eval(%Q{
-
-
1
def #{association_name}_with_initialization
-
14
#{association_name}_without_initialization ||
-
build_#{association_name}
-
end
-
-
1
alias_method_chain(:#{association_name}, :initialization)
-
-
1
def validating_ena_required_fields=(state)
-
@validating_ena_required_fields = !!state
-
self.#{ association_name }.validating_ena_required_fields = state
-
end
-
-
1
def validating_ena_required_fields?
-
@validating_ena_required_fields
-
20
end
-
-
-
1
def tags
-
self.class.tags.select{|tag| tag.for?(accession_service.provider)}
-
end
-
-
1
def required_tags
-
self.class.required_tags[accession_service.try(:provider)]+self.class.required_tags[:all]
-
end
-
-
1
def self.tags
-
@tags ||= []
-
end
-
-
12
before_validation { |record| record.#{association_name } }
-
-
}, __FILE__, line)
-
-
1
def self.required_tags
-
@required_tags ||= Hash.new {|h,k| h[k] = Array.new }
-
end
-
end
-
-
def include_tag(tag,options=Hash.new)
-
tags << AccessionedTag.new(tag,options[:as],options[:services],options[:downcase])
-
end
-
-
def require_tag(tag,services=:all)
-
[services].flatten.each do |service|
-
required_tags[service] << tag
-
end
-
end
-
-
-
class AccessionedTag
-
attr_reader :tag, :name, :downcase
-
def initialize(tag, as=nil, services=[],downcase=false)
-
@tag = tag
-
@name = as||tag
-
@services = [services].flatten.compact
-
@downcase = downcase
-
end
-
-
def for?(service)
-
@services.empty? || @services.include?(service)
-
end
-
end
-
-
def construct_metadata_class(table_name, as_class, &block)
-
# Build the new metadata model
-
metadata = Class.new( self == as_class ? Base : as_class::Metadata)
-
metadata.instance_eval(&block) if block_given?
-
-
as_name = as_class.name.demodulize.underscore
-
-
# Ensure that it is correctly associated back to the owner model and that the table name
-
# is correctly set.
-
metadata.instance_eval %Q{
-
self.table_name =('#{table_name}')
-
belongs_to :#{as_name}, :class_name => #{ self.name.inspect }, :validate => false, :autosave => false
-
belongs_to :owner, :foreign_key => :#{as_name}_id, :class_name => #{self.name.inspect}, :validate => false, :autosave => false, :inverse_of => :#{as_name}_metadata
-
}
-
-
# Finally give it a name!
-
const_set(:Metadata, metadata)
-
end
-
-
class Base < ActiveRecord::Base
-
# All derived classes have their own table. We're just here to help with some behaviour
-
self.abstract_class = true
-
-
# This ensures that the default values are stored within the DB, meaning that this information will be
-
# preserved for the future, unlike the original properties information which didn't store values when
-
# nil which lead to us having to guess.
-
def initialize(attributes = {}, *args, &block)
-
6
super(self.class.defaults.merge(attributes.try(:symbolize_keys) || {}),*args, &block)
-
end
-
-
before_validation :merge_instance_defaults, :on => :create
-
-
def merge_instance_defaults
-
# Replace attributes with the default if the value is nil
-
6
self.attributes = instance_defaults.merge(self.attributes.symbolize_keys) {|key, default, attribute| attribute.nil? ? default : attribute}
-
end
-
-
include Attributable
-
-
def validating_ena_required_fields?
-
@validating_ena_required_fields
-
22
end
-
-
def validating_ena_required_fields=(state)
-
@validating_ena_required_fields = !!state
-
end
-
-
delegate :validator_for, :to => :owner
-
-
def service_specific_fields
-
owner.required_tags.uniq.select do |tag|
-
owner.errors.add(:base,"#{tag} is required") if send(tag).blank?
-
end.empty?
-
end
-
-
class << self
-
-
def metadata_attribute_path_store
-
@md_a_p ||= Hash.new {|h,field| h[field] = metadata_attribute_path_generator(field) }
-
end
-
-
def metadata_attribute_path_generator(field)
-
self.name.underscore.split('/').map(&:to_sym) + [ field.to_sym ]
-
end
-
-
def metadata_attribute_path(field)
-
metadata_attribute_path_store[field]
-
end
-
-
def localised_sections_store
-
@loc_sec ||= Hash.new {|h,field| h[field] = localised_sections_generator(field) }
-
end
-
-
def localised_sections_generator(field)
-
Section.new(
-
* (SECTION_FIELDS.map do |section|
-
I18n.t(
-
section,
-
:scope => [ :metadata, metadata_attribute_path(field) ].flatten,
-
:default => I18n.t(section, :scope => [ :metadata, :defaults ])
-
)
-
end << {})
-
)
-
end
-
-
def localised_sections(field)
-
localised_sections_store[field]
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
class Metadata::BuilderBase < ActionView::Helpers::FormBuilder
-
attr_writer :locals
-
-
def initialize(*args, &block)
-
super
-
@views, @locals, @root, @filter = { }, { }, nil, ->(_) { true }
-
end
-
-
def view_for(type, partial_name = nil, &block)
-
@views[type.to_sym] =
-
if partial_name.nil?
-
{ :inline => capture(&block) }
-
else
-
{ :partial => partial_name }
-
end
-
end
-
-
def filter(&block)
-
@filter = block
-
end
-
-
private
-
1
-
delegate :concat, :capture, :render, :content_tag, :to => :@template
-
-
#--
-
# NOTE: Ripped directly from InstanceTag in form_helper.rb
-
#++
-
1
def sanitized_object_name
-
@object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
-
end
-
-
1
def localised_sections(field)
-
sections = @object.class.localised_sections(field).dup
-
sections.label_options = { :class => 'required' } if @object.required?(field)
-
sections
-
end
-
-
1
def render_view(type, field, options = {}, &block)
-
return nil unless @filter.call(@object.class.metadata_attribute_path(field))
-
view = @views[type.to_sym] or raise StandardError, "View not registered for '#{ type }'"
-
-
locals = @locals.merge(
-
:sections => localised_sections(field),
-
:form => self,
-
:field_name => field,
-
:group => nil,
-
:value => @object.send(field)
-
)
-
locals[:group] = options[:grouping].downcase.gsub(/[^a-z0-9]+/, '_') unless options[:grouping].blank?
-
locals = yield(locals) if block_given?
-
-
render(view.merge(:locals => locals))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Metadata::FormBuilder < Metadata::BuilderBase
-
1
def initialize(*args, &block)
-
super
-
view_for(:field, 'shared/metadata/edit_field')
-
view_for(:header, 'shared/metadata/header')
-
view_for(:document, 'shared/metadata/edit_document_field')
-
view_for(:checktext, 'shared/metadata/edit_checktext_field')
-
-
@related_fields, @changing = [], []
-
end
-
-
# Creates a file upload field that will be properly handled by Document instances. It's a bit of
-
# a hack because the field we're actually rendering is not what is requested in 'field', but
-
# 'field_attributes[uploaded_data]', so we have to also use a special view for it to alter that.
-
#--
-
# NOTE: This is immediately overridden by the block below so don't move it!
-
#++
-
1
def document_field(field, options = {})
-
fields_for(:"#{ field }_attributes", :builder => ActionView::Helpers::FormBuilder) do |fields|
-
fields.file_field(:uploaded_data)
-
end
-
end
-
-
1
def select_by_association(association, options={},html_options={})
-
association_target, options = association.to_s.classify.constantize, { }
-
options[:selected] = association_target.default.for_select_dropdown.last if @object.send(association).nil? and association_target.default.present?
-
select(:"#{association}_id", association_target.for_select_association, options,html_options)
-
end
-
-
1
def checktext_field(field, options = {})
-
render_view(:checktext, field, options)
-
end
-
-
1
[:text_area, :text_field, :number_field].each do |field|
-
3
class_eval <<-END_OF_METHOD
-
3
def #{ field }_with_bootstrap(*args, &block)
-
options = args.extract_options!
-
options[:class] ||= ''
-
options[:class] << ' form-control'
-
args.push(options)
-
3
#{ field }_without_bootstrap(*args, &block)
-
end
-
END_OF_METHOD
-
3
alias_method_chain(field, :bootstrap)
-
end
-
-
1
def select_with_bootstrap(method, choices, options={}, html_options={}, &block)
-
html_options[:class] ||= ''
-
html_options[:class] << ' form-control'
-
select_without_bootstrap(method, choices, options, html_options, &block)
-
end
-
1
alias_method_chain(:select, :bootstrap)
-
-
-
# We wrap each of the following field types (text_field, select, etc) within a special
-
# layout for our properties
-
#
-
{
-
1
:document_field => :document,
-
:text_area => :field,
-
:text_field => :field,
-
:number_field => :field,
-
:select => :field,
-
:file_field => :field,
-
:check_box => :field,
-
:checktext_field => :field
-
}.each do |field, type|
-
8
class_eval <<-END_OF_METHOD
-
8
def #{ field }_with_property_field_wrapper(method, *args, &block)
-
options = args.extract_options!
-
field_args = options.slice(:grouping)
-
args.push(options.slice!(:grouping))
-
8
property_field(#{ type.inspect }, method, field_args) do
-
8
#{ field }_without_property_field_wrapper(method, *args, &block)
-
end
-
end
-
END_OF_METHOD
-
8
alias_method_chain(field, :property_field_wrapper)
-
end
-
-
1
def header(field, options = {})
-
render_view(:header, field, options)
-
end
-
-
# Handles wrapping certain fields so that they are only shown when another field is a certain value.
-
# You can use `:to` to give the name of the field, `:when` for the single value when the fields should
-
# be shown, and `:in` for a group of values. You *must* call finalize_related_fields at the end of
-
# your view to get the appropriate behaviour
-
1
def related_fields(options, &block)
-
options.symbolize_keys!
-
-
values = (options.fetch(:in, Array(options[:when])) - Array(options[:not])).map { |v| v.to_s.downcase.gsub(/[^a-z0-9]+/, '_') }
-
content = capture(&block)
-
concat(content_tag(:div, content, :class => [ :related_to, options[:to], values ].flatten.join(' ')))
-
-
@related_fields.push(options[:to])
-
content
-
end
-
-
# Allows the options of the specified 'field' to be changed based on the value of another field.
-
1
def change_select_options_for(field, options)
-
options[:values] = options[:values].inject({}) do |values, (key, value)|
-
values.tap do
-
Array(key).each { |k| values[k.to_s] = Array(value) }
-
end
-
end
-
@changing.push([ field, options ])
-
end
-
-
# Renders the Javascript for dealing with showing and hiding the related fields.
-
1
def finalize_related_fields(&block)
-
related = @related_fields.compact.uniq.map(&:to_s)
-
concat(render(
-
:partial => 'shared/metadata/related_fields',
-
:locals => {
-
:root => sanitized_object_name,
-
:related => related,
-
:changing_fields => @changing
-
}
-
)) unless related.empty?
-
end
-
-
1
private
-
-
1
def property_field(type, field, options = {}, &block)
-
content = capture do
-
render_view(type, field, options) { |locals| locals.merge(:field => yield) }
-
end
-
-
div_options = { :id => field.to_s }
-
(div_options[:class] ||= []) << 'field_with_errors' unless @object.errors.get(field).blank?
-
content_tag(:div, content, div_options)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class Metadata::ViewBuilder < Metadata::BuilderBase
-
1
def initialize(*args, &block)
-
super
-
view_for(:plain_value, 'shared/metadata/plain_field')
-
view_for(:file, 'shared/metadata/file')
-
end
-
-
1
def plain_value(field, options = {})
-
render_view(:plain_value, field, options) { |locals| locals.merge(:value => @object.send(field)) }
-
end
-
-
1
def yes_or_no(field, options = {})
-
render_view(:plain_value, field, options) { |locals| locals.merge(:value => @object.send(field).present? ? "Yes" : "No") }
-
end
-
-
1
def file(field, options = {})
-
render_view(:file, field, options) { |locals| locals.merge(:document => @object.send(field)) }
-
end
-
-
1
def association_attribute(association_name, attribute, options = {})
-
render_view(:plain_value, :"#{association_name}_id", options) { |locals| locals.merge(:value => @object.try(association_name).try(attribute)) }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class MiSeqSequencingRequest < SequencingRequest
-
1
include Request::CustomerResponsibility
-
-
1
def flowcell_identifier
-
"Cartridge barcode"
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class MovieLengthTask < Task
-
1
class MovieLengthData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && MovieLengthData.new(request)
-
end
-
-
1
def partial
-
"movie_length_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_movie_length_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_movie_length_task(self, params)
-
end
-
-
1
def valid_movie_length?(movie_length)
-
return false if movie_length.blank?
-
movie_length.split(/,/).each do |movie_timing|
-
return false if (movie_timing.blank? || ! movie_timing.to_i.is_a?(Integer) || movie_timing.to_i < 0)
-
end
-
-
true
-
end
-
-
1
def included_for_do_task
-
[{:requests=>:asset}, :pipeline ]
-
end
-
-
1
def included_for_render_task
-
[{:requests=>:asset}, :pipeline ]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class MultiplexedCherrypickingTask < Task
-
1
include Tasks::PlatePurposeBehavior
-
-
1
belongs_to :purpose
-
-
1
class AssignTubesToWellsData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def partial
-
"assign_wells_to_wells"
-
end
-
-
1
def included_for_do_task
-
[{:requests=>:asset}, :pipeline ]
-
end
-
-
1
def included_for_render_task
-
[{:requests=>:asset}, :pipeline ]
-
end
-
-
1
def create_render_element(request)
-
request.asset && AssignTubesToWellsData.new(request)
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.set_plate_purpose_options(self)
-
end
-
-
1
def do_task(workflow, params)
-
destination_plate = target_plate(params[:existing_plate_barcode],params[:plate_purpose_id])
-
workflow.do_assign_requests_to_multiplexed_wells_task(self, params, destination_plate) &&
-
workflow.do_assign_pick_volume_task(self,params)
-
end
-
-
1
def target_plate(barcode,plate_purpose_id)
-
return Plate.with_machine_barcode(barcode).first unless barcode.blank?
-
PlatePurpose.find(plate_purpose_id).create!
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class MultiplexedLibraryCreationPipeline < LibraryCreationPipeline
-
1
include Pipeline::InboxGroupedBySubmission
-
-
1
INBOX_PARTIAL='request_group_by_submission_inbox'
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
# For a batch to be valid for completion in this pipeline it must have had the tags assigned to the
-
# target assets of the requests.
-
1
def validation_of_batch_for_completion(batch)
-
batch.errors.add(:base,'This batch appears to have not been properly tagged') if batch.requests.any? do |r|
-
r.target_asset.aliquots.map(&:tag).compact.size != r.target_asset.aliquots.size
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class MultiplexedLibraryCreationRequest < LibraryCreationRequest
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class MultiplexedLibraryTube < Tube
-
1
include ModelExtensions::MultiplexedLibraryTube
-
1
include Api::MultiplexedLibraryTubeIO::Extensions
-
1
include Transfer::Associations
-
-
# Transfer requests into a tube are direct requests where the tube is the target.
-
1
def transfer_requests
-
requests_as_target.where_is_a?(TransferRequest).all
-
end
-
-
# You can do sequencing with this asset type, even though the request types suggest otherwise!
-
1
def is_sequenceable?
-
true
-
end
-
-
1
def library_prep?
-
true
-
end
-
-
# Returns the type of asset that can be considered appropriate for request types.
-
1
def asset_type_for_request_types
-
LibraryTube
-
end
-
-
1
def creation_requests
-
direct = requests_as_target.where_is_a?(Request::LibraryCreation)
-
return direct unless direct.empty?
-
parents.find(:all,:include=>:creation_request).map(&:creation_request)
-
end
-
-
1
def team
-
creation_requests.first.request_type.try(:product_line).try(:name)
-
end
-
-
1
def library_source_plates
-
purpose.library_source_plates(self)
-
end
-
-
1
def self.stock_asset_type
-
StockMultiplexedLibraryTube
-
1
end
-
-
1
def self.stock_asset_purpose
-
Tube::Purpose.stock_mx_tube
-
end
-
-
1
extend Asset::Stock::CanCreateStockAsset
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class NoAccessionService < AccessionService
-
-
1
def initialize(study)
-
@study_id = study.id
-
end
-
-
1
def provider; :NONE end
-
-
1
def submit(user, *accessionables)
-
raise AccessionService::NumberNotRequired, I18n.t(:not_applicable_study,:scope=>'accession_service.not_required')
-
end
-
-
1
def submit_sample_for_user(sample, user)
-
raise AccessionService::NumberNotRequired, I18n.t(:not_applicable_study_for_sample,:scope=>'accession_service.not_required',study_id:@study_id)
-
end
-
-
1
def submit_study_for_user(study, user)
-
raise AccessionService::NumberNotRequired, I18n.t(:not_applicable_study,:scope=>'accession_service.not_required')
-
end
-
-
1
def submit_dac_for_user(study, user)
-
raise AccessionService::NumberNotRequired, I18n.t(:not_applicable_study_for_dac,:scope=>'accession_service.not_required')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
class Order < ActiveRecord::Base
-
class OrderRole < ActiveRecord::Base
-
self.table_name =('order_roles')
-
end
-
-
module InstanceMethods
-
def complete_building
-
#nothing just so mixin can use super
-
end
-
end
-
include InstanceMethods
-
include Uuid::Uuidable
-
include Submission::AssetGroupBehaviour
-
include Submission::ProjectValidation
-
include Submission::RequestOptionsBehaviour
-
include Submission::AccessionBehaviour
-
include ModelExtensions::Order
-
-
include Workflowed
-
-
self.inheritance_column = "sti_type"
-
-
# Required at initial construction time ...
-
belongs_to :study
-
validates :study, :presence => true, :unless => :cross_study_allowed
-
-
belongs_to :project
-
validates :project, :presence => true, :unless => :cross_project_allowed
-
-
belongs_to :order_role, :class_name => 'Order::OrderRole'
-
delegate :role, :to => :order_role, :allow_nil => true
-
-
belongs_to :product
-
-
belongs_to :user
-
validates_presence_of :user
-
-
belongs_to :workflow, :class_name => 'Submission::Workflow'
-
validates_presence_of :workflow
-
-
has_many :requests, :inverse_of => :order
-
-
belongs_to :submission, :inverse_of => :orders
-
scope :include_for_study_view, -> { includes(:submission) }
-
#validates_presence_of :submission
-
-
before_destroy :is_building_submission?
-
after_destroy :on_delete_destroy_submission
-
-
def is_building_submission?
-
self.submission.building?
-
end
-
-
def on_delete_destroy_submission
-
if is_building_submission?
-
# After destroying an order, if it is the last order on it's submission
-
# destroy the submission too.
-
orders = self.submission.orders
-
submission.destroy unless orders.size > 1
-
return true
-
end
-
return false
-
end
-
-
serialize :request_types
-
validates_presence_of :request_types
-
-
serialize :item_options
-
-
validate :assets_are_appropriate
-
validate :no_consent_withdrawl
-
-
class AssetTypeError < StandardError
-
end
-
-
def cross_study_allowed; false; end
-
def cross_project_allowed; false; end
-
def cross_compatible?; false; end
-
-
def no_consent_withdrawl
-
return true unless all_samples.detect(&:consent_withdrawn?)
-
errors.add(:samples,"in this submission have had patient consent withdrawn.")
-
false
-
end
-
private :no_consent_withdrawl
-
-
def assets_are_appropriate
-
all_assets.each do |asset|
-
errors.add(:asset, "'#{asset.name}' is a #{asset.sti_type} which is not suitable for #{first_request_type.name} requests") unless is_asset_applicable_to_type?(first_request_type, asset)
-
end
-
return true if errors.empty?
-
false
-
end
-
private :assets_are_appropriate
-
-
# TODO: Figure out why eager loading aliquots/samples returns [] even when
-
# we limit order_assets to receptacles.
-
def samples
-
#naive way
-
assets.map(&:samples).flatten.uniq
-
end
-
-
def all_samples
-
# slightly less naive way
-
all_assets.map(&:samples).flatten.uniq
-
end
-
-
def all_assets
-
pull_assets_from_asset_group if assets.empty? && asset_group.present?
-
assets
-
end
-
-
scope :for_studies, ->(*args) { {:conditions => { :study_id => args[0]} } }
-
scope :with_plate_as_target, ->(plate) {
-
# Essentially :joins => {:requests=>{:target_asset=>:container_association}}
-
# But container_association only exists on wells
-
# Note: This is a basic update of the scope performed during the merge. Need to make more rails-threey
-
select("DISTINCT orders.*").
-
joins([
-
'INNER JOIN requests AS wpat_r ON wpat_r.order_id = orders.id',
-
'INNER JOIN container_associations ON container_associations.content_id = wpat_r.target_asset_id'
-
]).
-
where(['container_associations.container_id = ?',plate.id])
-
}
-
-
-
self.per_page = 500
-
scope :including_associations_for_json, -> {
-
includes([
-
:uuid_object,
-
{:assets => [:uuid_object] },
-
{ :project => :uuid_object },
-
{ :study => :uuid_object },
-
:user
-
])
-
}
-
-
-
def self.render_class
-
Api::OrderIO
-
end
-
-
def url_name
-
"order"
-
end
-
alias_method(:json_root, :url_name)
-
-
def asset_uuids
-
assets.select{ |asset| asset.present? }.map(&:uuid) if assets
-
end
-
-
# TODO[xxx]: I don't like the name but this should disappear once the UI has been fixed
-
def self.prepare!(options)
-
constructor = options.delete(:template) || self
-
constructor.create_order!(options.merge(:assets => options.fetch(:assets, [])))
-
end
-
-
class << self
-
alias_method :create_order!, :create!
-
end
-
-
# only needed to note
-
def self.build!(options)
-
#call submission with appropriate Order subclass
-
Submission.build!({:template => self}.merge(options))
-
end
-
-
def self.extended(base)
-
class_eval do
-
def self.build!(*args)
-
Order::build!(*args)
-
end
-
end
-
end
-
-
-
def multiplexed?
-
RequestType.find(self.request_types).any?(&:for_multiplexing?)
-
end
-
-
def is_asset_applicable_to_type?(request_type, asset)
-
request_type.asset_type == asset.asset_type_for_request_types.name
-
end
-
private :is_asset_applicable_to_type?
-
1
-
-
delegate :left_building_state?, :to => :submission, :allow_nil => true
-
-
1
def create_request_of_type!(request_type, attributes = {}, &block)
-
em = request_type.extract_metadata_from_hash(request_options)
-
request_type.create!(attributes) do |request|
-
request.submission_id = submission_id
-
request.workflow = workflow
-
request.study = study
-
request.initial_project = project
-
request.user = user
-
request.request_metadata_attributes = em
-
request.state = initial_request_state(request_type)
-
request.order = self
-
-
if request.asset.present?
-
raise AssetTypeError, "Asset type does not match that expected by request type." unless is_asset_applicable_to_type?(request_type, request.asset)
-
end
-
end
-
end
-
-
1
def duplicate(&block)
-
create_parameters = template_parameters
-
new_order = Order.create(create_parameters.merge( :study => self.study,:workflow => self.workflow,
-
:user => self.user, :assets => self.assets, :state => self.state,
-
:request_types => self.request_types,
-
:request_options => self.request_options,
-
:comments => self.comments,
-
:project_id => self.project_id), &block)
-
new_order.save
-
return new_order
-
end
-
-
-
# attributes which are not saved for a submission but can be pre-set via SubmissionTemplate
-
# return a list of request_types lists (a sequence of choices) to display in the new view
-
1
attr_writer :request_type_ids_list
-
-
1
def request_type_ids_list; @request_type_ids_list ||= [[]]; end
-
-
1
attr_accessor :info_differential # aggrement text to display when creating a new submission
-
1
attr_accessor :customize_partial # the name of a partial to render.
-
1
DefaultAssetInputMethods = ["select an asset group"]
-
#DefaultAssetInputMethods = ["select an asset group", "enter a list of asset ids", "enter a list of asset names", "enter a list of sample names"]
-
1
attr_writer :asset_input_methods
-
1
def asset_input_methods; @asset_input_methods ||= DefaultAssetInputMethods; end
-
-
# return a hash with the values needed to be saved as a template
-
# beware nil values are filtered to not overwride default value set in the initializer
-
# (in case these default value are added after a template has been save)
-
# So don't forget to filter again if you override this method.
-
1
def template_parameters
-
{
-
:request_options => request_options,
-
:request_types => request_types,
-
:comments => comments,
-
:request_type_ids_list => request_type_ids_list,
-
:workflow_id => workflow.id,
-
:info_differential => info_differential,
-
:customize_partial => customize_partial,
-
:asset_input_methods => asset_input_methods != DefaultAssetInputMethods ? asset_input_methods : nil
-
}.reject { |k,v| v.nil?}
-
end
-
-
1
def request_types_list
-
request_type_ids_list.map { |ids| RequestType.find(ids) }
-
end
-
-
1
def first_request_type
-
RequestType.find(request_types.first)
-
end
-
-
1
def filter_asset_groups(asset_groups)
-
return asset_groups
-
end
-
-
1
class CompositeAttribute
-
1
attr_reader :display_name, :key, :default, :options
-
1
def initialize(key)
-
@key = key
-
end
-
1
def add(attribute,metadata)
-
@display_name ||= attribute.display_name
-
@key = attribute.assignable_attribute_name
-
@default ||= attribute.find_default(nil,metadata)
-
@kind = attribute.kind if @kind.nil?||attribute.required?
-
if attribute.selection?
-
new_options = attribute.selection_options(metadata)
-
@options ||= new_options if selection?
-
@options &= new_options
-
end
-
end
-
1
def kind
-
@kind||FieldInfo::TEXT
-
end
-
1
def selection?
-
kind==FieldInfo::SELECTION
-
end
-
1
def to_field_infos
-
values = {
-
:display_name => display_name,
-
:key => key,
-
:default_value => default,
-
:kind => kind
-
}
-
values.update(:selection => options) if self.selection?
-
FieldInfo.new(values)
-
end
-
end
-
-
1
def request_attributes
-
attributes = ActiveSupport::OrderedHash.new {|hash,value| hash[value] = CompositeAttribute.new(value) }
-
request_types_list.flatten.each do |request_type|
-
mocked = mock_metadata_for(request_type)
-
request_type.request_class::Metadata.attribute_details.each do |att|
-
attributes[att.name].add(att,mocked)
-
end
-
request_type.request_class::Metadata.association_details.each do |att|
-
attributes[att.name].add(att,nil)
-
end
-
end
-
-
attributes.values
-
end
-
-
1
def mock_metadata_for(request_type)
-
# We have to create a mocked Metadata to point back at the appropriate request class, as request options
-
# are no longer hardcoded in RequestClasses. This is a bit messy, but the tendrils of the old system went
-
# deep. In hindsight it would probably have been easier to either:
-
# a) Start from scratch
-
# b) Not bother
-
mock_request = request_type.request_class.new(:request_type=>request_type)
-
request_type.request_class::Metadata.new(:request=>mock_request,:owner=>mock_request)
-
end
-
-
# Return the list of input fields to edit when creating a new submission
-
# Unless you are doing something fancy, fall back on the defaults
-
1
def input_field_infos
-
return @input_field_infos if @input_field_infos
-
return @cache_calc ||= compute_input_field_infos
-
end
-
-
# we don't call it input_field_infos= because it has a slightly different meanings
-
# if input_field_infos is computed it override the computation
-
# this is meant do be used only when creating submission template
-
1
def set_input_field_infos(infos)
-
@input_field_infos = infos
-
end
-
-
-
1
def initial_request_state(request_type)
-
(request_options || {}).fetch(:initial_state, {}).fetch(request_type.id, request_type.initial_state).to_s
-
end
-
1
private :initial_request_state
-
-
1
def next_request_type_id(request_type_id)
-
request_type_ids = request_types.map(&:to_i)
-
request_type_ids[request_type_ids.index(request_type_id)+1]
-
end
-
-
-
1
def compute_input_field_infos
-
request_attributes.uniq.map do |combined|
-
combined.to_field_infos
-
end
-
end
-
1
protected :compute_input_field_infos
-
-
# Are we still able to modify this instance?
-
1
def building?
-
self.submission.nil?
-
end
-
-
-
# Returns true if this is an order for sequencing
-
1
def is_a_sequencing_order?
-
[
-
PacBioSequencingRequest,
-
SequencingRequest,
-
*SequencingRequest.descendants
-
].include?(RequestType.find(request_types.last).request_class)
-
end
-
-
1
def collect_gigabases_expected?
-
input_field_infos.any? {|k| k.key==:gigabases_expected}
-
end
-
-
1
def add_comment(comment_str, user)
-
update_attribute(:comments, [comments, comment_str ].compact.join('; '))
-
save!
-
-
submission.requests.where_is_not_a?(TransferRequest).for_order_including_submission_based_requests(self).map do |request|
-
request.add_comment(comment_str, user)
-
end
-
end
-
-
1
def friendly_name
-
asset_group.try(:name)||asset_group_name||id
-
end
-
-
1
def subject_type
-
'order'
-
end
-
-
1
def generate_broadcast_event
-
BroadcastEvent::OrderMade.create!(:seed=>self,:user=>user)
-
end
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
require 'pac_bio_json_format'
-
1
class PacBio::PrimaryApi::Base < ActiveResource::Base
-
1
self.site = configatron.pac_bio_instrument_api.to_s
-
1
self.format = :pac_bio_json
-
1
self.proxy = configatron.proxy.to_s if configatron.proxy
-
-
1
def self.collection_path(*args, &block)
-
super.sub(/\.json/, '')
-
end
-
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class PacBio::SampleSheet
-
1
def header_metadata(batch)
-
[
-
['Version', '1.0.0', nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
['Unique ID', batch.id, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
['Type', 'Plate', nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
['Owner', 'System', nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
['Created By', batch.user.login, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
['Comments', "New plate created on #{Time.now}", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
['Output Path', nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil],
-
[nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
-
]
-
end
-
-
1
def column_headers
-
['Well No.', 'Sample Name', 'DNA Template Prep Kit Box Barcode', 'Prep Kit Parameters', 'Binding Kit Box Barcode', 'Binding Kit Parameters',
-
'Collection Protocol', 'CP Parameters', 'Basecaller', 'Basecaller Parameters', 'Secondary Analysis Protocol', 'Secondary Analysis Parameters',
-
'Sample Comments', 'User Field 1', 'User Field 2', 'User Field 3', 'User Field 4', 'User Field 5', 'User Field 6', 'Results Data Output Path']
-
end
-
-
-
1
def create_csv_from_batch(batch)
-
csv_string = CSV.generate( :row_sep => "\r\n") do |csv|
-
header_metadata(batch).each{ |header_row| csv << header_row }
-
csv << column_headers
-
requests_by_wells(batch).each do |requests|
-
csv << row(requests, batch)
-
end
-
end
-
end
-
-
1
def requests_by_wells(batch)
-
requests = batch.requests.for_pacbio_sample_sheet
-
sorted_well_requests = requests.group_by {|r| r.target_asset.map.column_order }.sort
-
sorted_well_requests.map {|well_index,requests| requests }
-
end
-
-
1
def replace_non_alphanumeric(protocol)
-
protocol.gsub(/[^\w]/,'_')
-
end
-
-
1
@@CONCAT_SEPARATOR = ';'
-
-
1
def concat(list, sym, separator=@@CONCAT_SEPARATOR)
-
list.map(&sym).uniq.join(separator)
-
end
-
-
-
1
def row(requests, batch)
-
# Read these lines when secondary analysis activated
-
# replace_non_alphanumeric(library_tube.pac_bio_library_tube_metadata.protocol),
-
# "JobName=DefaultJob_#{Time.now}",
-
#request = requests.first
-
-
library_tubes = requests.map(&:asset)
-
library_tubes_metadata = library_tubes.map(&:pac_bio_library_tube_metadata)
-
first_tube_metadata = library_tubes_metadata.first
-
first_request_metadata = requests.first.request_metadata
-
-
well = requests.first.target_asset
-
[
-
Map.pad_description(well.map),
-
concat(library_tubes, :name, '-'),
-
first_tube_metadata.prep_kit_barcode,
-
nil,
-
first_tube_metadata.binding_kit_barcode,
-
nil,
-
lookup_collection_protocol(requests.first),
-
"AcquisitionTime=#{first_tube_metadata.movie_length}|InsertSize=#{first_request_metadata.insert_size}|StageHS=True|SizeSelectionEnabled=False|Use2ndLook=False|NumberOfCollections=#{requests.size}",
-
'Default',
-
nil,
-
nil,
-
nil,
-
nil,
-
well.uuid,
-
concat(library_tubes, :uuid),
-
batch.uuid,
-
well.plate.barcode,
-
concat(requests, :uuid),
-
nil,
-
nil
-
]
-
end
-
-
1
def lookup_collection_protocol(request)
-
return 'Standard Seq v3' if request.request_metadata.sequencing_type == 'Standard'
-
return 'MagBead Standard Seq v2' if request.request_metadata.sequencing_type == 'MagBead'
-
request.request_metadata.sequencing_type
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
require 'pac_bio_json_format'
-
1
class PacBio::SecondaryApi::Base < ActiveResource::Base
-
1
self.site = configatron.pac_bio_smrt_portal_api.to_s
-
1
self.format = :pac_bio_json
-
1
self.proxy = configatron.proxy.to_s if configatron.proxy
-
-
1
def self.collection_path(*args, &block)
-
super.sub(/\.json/, '')
-
end
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PacBio::SecondaryApi::CustomField < PacBio::SecondaryApi::Base
-
1
self.element_name = "CustomFields"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PacBio::SecondaryApi::Input < PacBio::SecondaryApi::Base
-
1
self.element_name = "Inputs"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PacBio::SecondaryApi::Job < PacBio::SecondaryApi::Base
-
1
self.element_name = "Jobs"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PacBio::SecondaryApi::Protocol < PacBio::SecondaryApi::Base
-
1
self.element_name = "Protocols"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PacBio::SecondaryApi::ReferenceSequence < PacBio::SecondaryApi::Base
-
1
self.element_name = "ReferenceSequences"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# needs auth
-
1
class PacBio::SecondaryApi::User < PacBio::SecondaryApi::Base
-
1
self.element_name = "Users"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2014 Genome Research Ltd.
-
1
class PacBio::Worksheet
-
-
1
def initialize
-
end
-
-
1
def create_csv_from_batch(batch)
-
csv_string = CSV.generate( :row_sep => "\r\n") do |csv|
-
header_metadata(batch).each{ |header_row| csv << header_row }
-
csv << column_headers
-
batch.requests.each_with_index do |request,index|
-
csv << ( row(request))
-
end
-
end
-
end
-
-
1
protected
-
-
1
def header_metadata(batch)
-
[
-
["Batch #{batch.id}"],
-
["Sample", "", "Fragmentation", "", "End repair and ligation","","","","QC","",""]
-
]
-
end
-
-
1
def column_headers
-
["Well", "Name", "Required size", "Complete?", "Repaired?", "Adapter ligated?", "Clean up complete?", "Exonnuclease cleanup", "ng/ul", "Fragment size", "Volume"]
-
end
-
-
1
def row(request)
-
[
-
request.asset.display_name,
-
request.asset.primary_aliquot.sample.name,
-
request.request_metadata.insert_size,
-
'',
-
'',
-
'',
-
'',
-
'',
-
'',
-
'',
-
''
-
]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class PacBioLibraryTube < Tube
-
1
include Api::PacBioLibraryTubeIO::Extensions
-
-
1
extend Metadata
-
1
has_metadata do
-
1
attribute(:prep_kit_barcode)
-
1
attribute(:binding_kit_barcode)
-
1
attribute(:smrt_cells_available)
-
1
attribute(:movie_length)
-
end
-
-
-
1
def protocols_for_select
-
ReferenceGenome.sorted_by_name.map { |x| [x.name, x.id]}.tap do |protocols|
-
reference_genome = primary_aliquot.sample.sample_reference_genome
-
protocols.unshift([reference_genome.name, reference_genome.id]) if reference_genome.present?
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class PacBioSamplePrepPipeline < Pipeline
-
1
INBOX_PARTIAL = 'group_by_parent'
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
1
def allow_tag_collision_on_tagging_task?
-
false
-
end
-
-
1
def inbox_eager_loading
-
:loaded_for_grouped_inbox_display
-
end
-
-
1
def post_release_batch(batch, user)
-
cancel_sequencing_requests_on_library_failure(batch)
-
cancel_excess_sequencing_requests(batch)
-
end
-
-
1
def cancel_downstream_requests(request)
-
request.next_requests(self).each{ |sequencing_request| sequencing_request.cancel_from_upstream! }
-
end
-
-
1
def cancel_sequencing_requests_on_library_failure(batch)
-
batch.requests.each do |request|
-
if request.failed?
-
cancel_downstream_requests(request)
-
end
-
end
-
end
-
-
1
def cancel_excess_sequencing_requests(batch)
-
batch.requests.each do |request|
-
smrt_cells_available = request.target_asset.pac_bio_library_tube_metadata.smrt_cells_available
-
smrt_cells_requested = number_of_smrt_cells_requested(request)
-
next if smrt_cells_available.nil? || smrt_cells_requested.nil?
-
if smrt_cells_available < smrt_cells_requested
-
cancel_excess_downstream_requests(request, (smrt_cells_requested - smrt_cells_available))
-
end
-
-
end
-
end
-
-
1
def cancel_excess_downstream_requests(request, number_to_cancel)
-
request.next_requests(self).each_with_index do |sequencing_request, index|
-
sequencing_request.cancel_from_upstream! if index < number_to_cancel
-
end
-
end
-
-
1
def number_of_smrt_cells_requested(request)
-
request.next_requests(self).count
-
end
-
-
# PacBio pipelines do not require their batches to record the position of their requests.
-
1
def requires_position?
-
false
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2014,2015 Genome Research Ltd.
-
1
class PacBioSamplePrepRequest < CustomerRequest
-
-
1
has_metadata :as => Request do
-
1
attribute(:insert_size)
-
1
attribute(:sequencing_type)
-
end
-
1
include Request::CustomerResponsibility
-
-
1
class RequestOptionsValidator < DelegateValidation::Validator
-
end
-
-
1
def self.delegate_validator
-
PacBioSamplePrepRequest::RequestOptionsValidator
-
end
-
-
1
private
-
-
1
def on_started
-
target_asset.generate_name(asset.display_name.gsub(':','-'))
-
target_asset.save
-
end
-
-
1
def on_passed
-
final_transfer.pass!
-
end
-
-
1
def on_failed
-
final_transfer.fail!
-
end
-
-
1
def final_transfer
-
target_asset.requests_as_target.where_is_a?(TransferRequest).last
-
end
-
-
1
class Initial < TransferRequest
-
1
include TransferRequest::InitialTransfer
-
1
def outer_request
-
asset.requests.detect{|r| r.is_a?(PacBioSamplePrepRequest)}
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class PacBioSequencingPipeline < Pipeline
-
1
include Pipeline::InboxGroupedBySubmission
-
-
1
INBOX_PARTIAL = 'pac_bio_sequencing_inbox'
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
# PacBio pipelines do not require their batches to record the position of their requests.
-
1
def requires_position?
-
false
-
end
-
-
1
def post_release_batch(batch, user)
-
batch.requests.each(&:transfer_aliquots)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2014,2015 Genome Research Ltd.
-
1
class PacBioSequencingRequest < CustomerRequest
-
-
1
has_metadata :as => Request do
-
1
attribute(:insert_size, :validator => true, :required => true, :integer => true, :selection =>true )
-
1
attribute(:sequencing_type, :validator => true, :required => true, :selection =>true )
-
end
-
-
1
include Request::CustomerResponsibility
-
-
1
class RequestOptionsValidator < DelegateValidation::Validator
-
1
delegate_attribute :insert_size, :to => :target, :type_cast => :to_i
-
1
validates_numericality_of :insert_size, :integer_only => true, :greater_than => 0
-
-
# validates_inclusion_of :insert_size, :in => PacBioSequencingRequest::INSERT_SIZE
-
-
1
delegate_attribute :sequencing_type, :to => :target
-
end
-
-
1
def self.delegate_validator
-
PacBioSequencingRequest::RequestOptionsValidator
-
end
-
-
1
def on_started
-
-
end
-
-
# Returns a list of attributes that must match for any given pool
-
1
def shared_attributes
-
"MovieLength:#{asset.pac_bio_library_tube_metadata.movie_length};InsertSize:#{request_metadata.insert_size};SequencingType:#{request_metadata.sequencing_type};"
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
require 'csv'
-
1
module Parsers
-
-
1
def self.parser_for(filename, content_type, content)
-
return nil unless filename.ends_with?('.csv') || content_type == 'text/csv'
-
# While CSV tries to detect line endings, it isn't so great with some excel
-
# exported CSVs, where a mix of \n and \r\n are used in the same document
-
# This converts everything to \n before processing
-
cleaned_content = content.gsub(/\r\n?/,"\n")
-
csv = CSV.parse(cleaned_content)
-
return Parsers::BioanalysisCsvParser.new(csv) if Parsers::BioanalysisCsvParser.is_bioanalyzer?(csv)
-
return Parsers::ISCXTenParser.new(csv) if Parsers::ISCXTenParser.is_isc_xten_file?(csv)
-
nil
-
end
-
-
end
-
# encoding: utf-8
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Parsers::BioanalysisCsvParser
-
-
1
class InvalidFile < StandardError; end
-
-
1
attr_reader :content
-
-
1
def initialize(content)
-
@content = content
-
end
-
-
1
def field_name_for(sym_name)
-
{
-
:concentration => "Conc. [ng/µl]",
-
:molarity => "Molarity [nmol/l]"
-
}[sym_name]
-
end
-
-
1
def concentration(plate_position)
-
return get_parsed_attribute(plate_position, field_name_for(:concentration))
-
end
-
-
1
def molarity(plate_position)
-
return get_parsed_attribute(plate_position, field_name_for(:molarity))
-
end
-
-
1
def table_content_hash(group)
-
content_hash = {}
-
starting_line = group[0]
-
ending_line = group[1]
-
type = content[starting_line][0]
-
fields = content[starting_line+1]
-
-
for pos in (starting_line+2) .. (ending_line) do
-
values = content[pos]
-
unless values.nil? && (values.length != fields.length)
-
content_hash.merge!(Hash[fields.zip(values)])
-
end
-
end
-
content_hash
-
end
-
-
1
def build_range(range)
-
if range == nil
-
range = [0, content.length-1]
-
else
-
range = range.dup
-
end
-
range.push(content.length-1) if (range.length==1)
-
range
-
end
-
-
# Finds groups of lines by range in which the beginning of the range contains the
-
# matching regexp as text in the first column and the end of the range is an empty line
-
# - regexp -> Regular expression to be matched in the first column as beginning of range
-
# - range -> In case it is specified, restricts the searching process to this range of lines
-
# instead of using all the content of the CSV file
-
1
def get_groups(regexp, range=nil)
-
groups = []
-
group = []
-
range = build_range(range)
-
-
group_contents = get_group_content(range)
-
-
group_contents.each_with_index do |line, pos|
-
-
if line[0].present? && line[0].match(regexp) && group.empty?
-
group.push(pos)
-
elsif (line.empty? && group.one? )
-
group.push(pos-1)
-
end
-
-
if group.length == 2
-
groups.push [group[0] + range[0], group[1] + range[0]]
-
group = []
-
end
-
if ((group.length ==1) && (pos==(group_contents.length-1)))
-
groups.push [group[0] + range[0], pos + range[0]]
-
end
-
end
-
groups
-
end
-
-
1
def get_group_content(group)
-
content.slice(group[0], group[1]-group[0]+1)
-
end
-
-
1
def parse_peak_table(group)
-
table_content_hash(get_groups(/Peak Table/m, group)[0])
-
end
-
-
1
def parse_region_table(group)
-
table_content_hash(get_groups(/Region Table/m, group)[0])
-
end
-
-
1
def parse_overall(group)
-
# Number of peaks
-
get_group_content(get_groups(/Overall.*/m, group)[0])[1][1]
-
end
-
-
1
def parse_cell(group)
-
get_group_content(group)[0][1]
-
end
-
-
1
def parse_sample(group)
-
{
-
parse_cell(group) => {
-
:peak_table => parse_peak_table(group),
-
:region_table => parse_region_table(group),
-
:overall => parse_overall(group)
-
}
-
}
-
end
-
-
1
def parse_samples
-
groups = get_groups(/Sample Name/)
-
-
groups.each_with_index.map do |group, pos|
-
if (pos == (groups.length - 1))
-
next_index = @content.length - 1
-
else
-
next_index = groups[pos+1][0]-1
-
end
-
[group[0], next_index]
-
end.reduce({}) do |memo, group|
-
memo.merge(self.parse_sample group)
-
end
-
end
-
-
1
def parsed_content
-
@parsed_content ||= parse_samples
-
rescue NoMethodError => e #Ugh! I want to catch these where they happen
-
raise InvalidFile
-
end
-
-
1
def get_parsed_attribute(plate_position, field)
-
return nil if parsed_content.nil? || parsed_content[plate_position].nil?
-
parsed_content[plate_position][:peak_table][field]
-
end
-
-
1
def each_well_and_parameters
-
parsed_content.each do |well,values|
-
yield(well,values[:peak_table][field_name_for(:concentration)],values[:peak_table][field_name_for(:molarity)])
-
end
-
end
-
-
-
1
def self.is_bioanalyzer?(content)
-
# We don't go through the whole file
-
content[0..10].detect do |line|
-
/Version Created/ === line[0] && /^B.*/ === line[1]
-
end.present?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class Parsers::ISCXTenParser
-
1
class InvalidFile < StandardError; end
-
-
1
def headers
-
@content[0]
-
end
-
-
1
def table
-
@content.slice(1, @content.length)
-
end
-
-
1
def get_row(location)
-
letter=location[0].chr
-
num = location.slice(1, location.length)
-
table.detect do |row|
-
row[0]==letter && row[1]== num
-
end
-
end
-
-
1
def concentration(location)
-
begin
-
get_row(location)[get_column_for_header(:concentration)]
-
rescue NoMethodError #Ugh! I want to catch these where they happen
-
raise InvalidFile
-
end
-
end
-
-
1
def get_column_for_header(sym)
-
headers.each_with_index do |h, pos|
-
name = get_name_for_header(sym)
-
unless name.nil? || h.nil?
-
return pos if h.squish == name.squish
-
end
-
end
-
end
-
-
1
def get_name_for_header(sym_name)
-
{
-
:row => "Well Row",
-
:col => "Well Col",
-
:content => "Content",
-
:raw_data => "Raw Data (485/520)",
-
:concentration => "Linear regression fit based on Raw Data (485/520)"
-
}[sym_name]
-
end
-
-
1
def self.is_isc_xten_file?(content)
-
parser = Parsers::ISCXTenParser.new(content)
-
[:row, :col, :content, :raw_data, :concentration].each_with_index.map do |sym, pos|
-
parser.get_column_for_header(sym) == pos
-
end.all?
-
end
-
-
1
def initialize(content)
-
@content = content
-
end
-
-
1
def locations
-
table.sort {|a,b| a[0] <=> b[0] && a[1].to_i <=> b[1].to_i}.map {|l| l[0]+l[1]}
-
end
-
-
1
def each_well_and_parameters
-
locations.each do |location_name|
-
yield(location_name, concentration(location_name))
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Permission < ActiveRecord::Base
-
1
belongs_to :permissable, :polymorphic => true
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PicoAssayAPlate < PicoAssayPlate
-
1
self.prefix = "PA"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PicoAssayBPlate < PicoAssayPlate
-
1
self.prefix = "PB"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014 Genome Research Ltd.
-
1
class PicoAssayPlate < Plate
-
1
self.prefix = "PA"
-
-
1
class WellDetail
-
1
attr_accessor :map, :parent_plate
-
-
1
def initialize(details, parent_plate)
-
@map = details[:map]
-
@concentration = details[:concentration]
-
@parent_plate = parent_plate
-
end
-
-
1
def target_map
-
Map.find_by_description_and_asset_size(map, parent_plate.stock_plate.size)
-
end
-
-
1
def target_well
-
@target_well ||= parent_plate.stock_plate.wells.find_by_map_id(target_map.id)
-
end
-
-
1
def concentration
-
@concentration > 0 ? @concentration : 0.0
-
end
-
-
# TODO this method needs to go, to be replace by direct calls
-
# to #grade_as_passed and #grade_as_failed
-
1
def grade_as!(state)
-
case state
-
when "passed" then grade_as_passed
-
when "failed" then grade_as_failed
-
end
-
-
update_well_concentraion!
-
end
-
-
1
def grade_as_passed
-
target_well.events.create_pico!('Pass')
-
target_well.well_attribute.pass_pico_test
-
end
-
-
1
def grade_as_failed
-
target_well.events.create_pico!('Fail')
-
target_well.well_attribute.fail_pico_test
-
end
-
-
1
def update_well_concentraion!
-
target_well.set_concentration(concentration)
-
end
-
end
-
-
-
1
def upload_pico_results(state, failure_reason, well_details)
-
return false if state.nil? || well_details.blank? || stock_plate().nil?
-
-
ActiveRecord::Base.transaction do
-
event = stock_plate.events.create_pico!(state)
-
# Adds a failure reason if it is available.
-
event.update_attributes(:descriptor_key => failure_reason) unless failure_reason.nil?
-
well_details.each { |details| WellDetail.new(details[:well], self).grade_as!(state) }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PicoAssayPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2015 Genome Research Ltd.
-
1
class PicoDilutionPlate < DilutionPlate
-
# self.per_page is set to a "highish" number so that the first page
-
# sent to PicoGreen is likely to hold all the rececent PicoDilutionPlates
-
1
self.per_page = 5000
-
1
self.prefix = "PD"
-
-
1
def self.index_to_hash(pico_dilutions)
-
pico_dilutions.map{ |pico_dilution| pico_dilution.to_pico_hash }
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
class PipelinesRequestType < ActiveRecord::Base
-
belongs_to :pipeline, :inverse_of => :pipelines_request_types
-
belongs_to :request_type, :inverse_of => :pipelines_request_types
-
-
validates_uniqueness_of :request_type_id, :scope => :pipeline_id
-
validates_presence_of :request_type, :pipeline
-
end
-
-
class Pipeline < ActiveRecord::Base
-
include ::ModelExtensions::Pipeline
-
include SequencingQcPipeline
-
include Uuid::Uuidable
-
include Pipeline::InboxUngrouped
-
include Pipeline::BatchValidation
-
-
INBOX_PARTIAL = 'default_inbox'
-
ALWAYS_SHOW_RELEASE_ACTIONS = false # Override this in subclasses if you want to display action links for released batches
-
-
self.inheritance_column = "sti_type"
-
1
-
delegate :item_limit, :has_batch_limit?, :to => :workflow
-
1
validates_presence_of :workflow
-
-
1
belongs_to :location
-
1
belongs_to :control_request_type, :class_name => 'RequestType'
-
1
belongs_to :next_pipeline, :class_name => 'Pipeline'
-
1
belongs_to :previous_pipeline, :class_name => 'Pipeline'
-
-
1
has_one :workflow, :class_name => "LabInterface::Workflow", :foreign_key => :pipeline_id
-
-
1
has_many :controls
-
1
has_many :pipeline_request_information_types
-
1
has_many :request_information_types, :through => :pipeline_request_information_types
-
1
has_many :tasks, :through => :workflows
-
1
has_many :pipelines_request_types, :inverse_of => :pipeline
-
1
has_many :request_types, :through => :pipelines_request_types, :validate => false
-
1
has_many :requests, :through => :request_types, :extend => [ Pipeline::InboxExtensions, Pipeline::RequestsInStorage]
-
1
has_many :batches do
-
1
def build(attributes = nil)
-
attributes ||= {}
-
attributes[:item_limit] = proxy_association.owner.workflow.item_limit
-
super(attributes)
-
end
-
end
-
-
1
validates_presence_of :request_types
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :on => :create, :message => "name already in use"
-
-
-
1
scope :externally_managed, -> { where( :externally_managed => true ) }
-
1
scope :internally_managed, -> { where( :externally_managed => false ) }
-
1
scope :active, -> { where( :active => true ) }
-
1
scope :inactive, -> { where( :active => false ) }
-
1
scope :alphabetical, -> { order('name ASC') }
-
-
1
scope :for_request_type, ->(rt) {
-
{
-
:joins => [ 'LEFT JOIN pipelines_request_types prt ON prt.pipeline_id = pipelines.id' ],
-
:conditions => ['prt.request_type_id = ?', rt.id]
-
}
-
}
-
-
1
def request_types_including_controls
-
[control_request_type].compact + request_types
-
end
-
-
1
def custom_inbox_actions
-
[]
-
end
-
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
1
def inbox_eager_loading
-
:loaded_for_inbox_display
-
end
-
-
1
def display_next_pipeline?
-
false
-
end
-
-
1
def requires_position?
-
true
-
end
-
-
#This needs to be re-done a better way
-
1
def qc?
-
false
-
end
-
-
1
def library_creation?
-
false
-
end
-
-
1
def genotyping?
-
false
-
end
-
-
1
def sequencing?
-
false
-
end
-
-
1
def is_read_length_consistent_for_batch?(batch)
-
true
-
end
-
-
# This is the old behaviour for every other pipeline.
-
1
def detach_request_from_batch(batch, request)
-
request.return_for_inbox!
-
self.update_detached_request(batch, request)
-
request.save!
-
end
-
-
1
def update_detached_request(batch, request)
-
request.remove_unused_assets
-
end
-
-
1
def get_input_request_groups(show_held_requests=true)
-
group_requests( inbox_scope_on(requests.inputs(show_held_requests).unbatched.send(inbox_eager_loading)))
-
end
-
-
1
def grouped_requests(show_held_requests=true)
-
inbox_scope_on(requests.inputs(show_held_requests).unbatched.send(inbox_eager_loading)).for_group_by(grouping_attributes)
-
end
-
-
1
def inbox_scope_on(inbox_scope)
-
custom_inbox_actions.inject(inbox_scope) { |context, action| context.send(action) }
-
end
-
1
private :inbox_scope_on
-
-
1
def hash_to_group_key(hash)
-
if hash.is_a? Array
-
group = hash
-
else
-
group = []
-
[:parent, :submission, :study].each do |s|
-
if self.send("group_by_#{s}?")
-
group << hash[s]
-
end
-
end
-
end
-
group.map {|e| e.to_i}
-
end
-
-
1
def grouping_function(option = {})
-
return ->(r) { [r.container_id] } if option[:group_by_holder_only]
-
-
lambda do |request|
-
[].tap do |group_key|
-
group_key << request.container_id if group_by_parent?
-
group_key << request.submission_id if group_by_submission?
-
end
-
end
-
end
-
1
private :grouping_function
-
-
1
def grouping_attributes
-
[].tap do |group_key|
-
group_key << 'hl.container_id' if group_by_parent?
-
group_key << 'requests.submission_id' if group_by_submission?
-
end
-
end
-
1
private :grouping_attributes
-
-
# to overwrite by subpipeline if needed
-
1
def group_requests(requests, option={})
-
requests.group_requests(:all, option).group_by(&grouping_function(option))
-
end
-
-
1
def group_key_to_hash(group)
-
group = group.dup # we don't want to modify the original group
-
hash = {}
-
[:parent, :submission, :study].each do |s|
-
if self.send("group_by_#{s}?")
-
hash[s] = group.shift
-
end
-
end
-
hash
-
end
-
-
1
def finish_batch(batch, user)
-
batch.complete!(user)
-
end
-
1
deprecate :finish_batch
-
-
1
def post_finish_batch(batch, user)
-
end
-
-
1
def completed_request_as_part_of_release_batch(request)
-
if self.library_creation?
-
unless request.failed?
-
EventSender.send_pass_event(request.id, "", "Passed #{self.name}.", self.id)
-
EventSender.send_request_update(request.id, "complete", "Completed pipeline: #{self.name}")
-
end
-
else
-
EventSender.send_request_update(request.id, "complete", "Completed pipeline: #{self.name}")
-
end
-
end
-
-
1
def release_batch(batch, user)
-
batch.release!(user)
-
end
-
1
deprecate :release_batch
-
-
1
def on_start_batch(batch, user)
-
# Do nothing
-
end
-
-
1
def post_release_batch(batch, user)
-
# Do Nothing
-
end
-
-
1
def has_controls?
-
self.controls.empty? ? false : true
-
end
-
-
1
def pulldown?
-
false
-
end
-
-
1
def purpose_information?
-
true
-
end
-
-
1
def prints_a_worksheet_per_task?
-
false
-
end
-
-
1
def grouping_parser(option = {})
-
grouper_class = option[:group_by_holder_only] ? GrouperByHolderOnly : GrouperForPipeline
-
grouper_class.new(self)
-
end
-
1
private :grouping_parser
-
-
1
def selected_values_from(browser_options)
-
browser_options.select { |_, v| v == '1' }
-
end
-
1
private :selected_values_from
-
-
1
def extract_requests_from_input_params(params ={})
-
if (request_ids = params["request"]).present?
-
requests.inputs(true).find(selected_values_from(request_ids).map(&:first), :order => 'id ASC')
-
elsif (selected_groups = params["request_group"]).present?
-
grouping_parser.all(selected_values_from(selected_groups))
-
else
-
raise StandardError, "Unknown manner in which to extract requests!"
-
end
-
end
-
-
1
def max_number_of_groups
-
self[:max_number_of_groups] || 0
-
end
-
-
1
def valid_number_of_checked_request_groups?(params ={})
-
return true if max_number_of_groups.zero?
-
return true if (selected_groups = params['request_group']).blank?
-
grouping_parser.count(selected_values_from(selected_groups)) <= max_number_of_groups
-
end
-
-
1
def all_requests_from_submissions_selected?(request_ids)
-
true
-
end
-
-
1
def can_create_stock_assets?
-
false
-
end
-
-
1
def request_actions
-
[:fail]
-
end
-
-
1
def allow_tag_collision_on_tagging_task?
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
1
module Pipeline::BatchValidation
-
1
def validation_of_batch(batch)
-
# Using throw and catch enables us to skip over the request validation without actually
-
# having to know whether it was needed or not.
-
catch(:no_requests_in_batch) do
-
validation_of_requests(batch.requests) do |message|
-
batch.errors.add(:requests, message)
-
end
-
end
-
end
-
-
1
def validation_of_requests(requests, &block)
-
throw :no_requests_in_batch if requests.blank?
-
yield('too many requests specified') if not max_size.nil? and requests.size > max_size
-
yield('has incorrect type') unless (requests.map(&:request_type_id) - request_types_including_controls.map(&:id)).empty?
-
end
-
1
private :validation_of_requests
-
-
# Overridden by pipeline implementations to ensure that the batch is valid for completion. By
-
# default this does nothing.
-
1
def validation_of_batch_for_completion(batch)
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
# Classes including this module are capable of grouping the requests for a pipeline in a
-
# specific manner.
-
module Pipeline::Grouper
-
1
def self.included(base)
-
base.delegate :requests, :group_by_parent?, :group_by_submission?, :to => :@pipeline
-
end
-
-
def initialize(pipeline)
-
@pipeline = pipeline
-
end
-
-
def all(selected_groups)
-
conditions, variables = [], []
-
selected_groups.each { |group, _| call(conditions, variables, group) }
-
requests.inputs(true).group_conditions(conditions, variables).group_requests(:all)
-
end
-
-
def count(selected_groups)
-
conditions, variables = [], []
-
selected_groups.each { |group, _| call(conditions, variables, group) }
-
requests.inputs(true).group_conditions(conditions, variables).group_requests(:count, :group => grouping)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Pipeline::GroupByHolderOnly
-
1
include Pipeline::Grouper
-
-
1
def call(conditions, variables, group)
-
conditions << "tca.container_id=?"
-
variables << group.to_i
-
end
-
1
private :call
-
-
1
def grouping
-
'tca.container_id'
-
end
-
1
private :grouping
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Pipeline::GrouperForPipeline
-
1
include Pipeline::Grouper
-
-
1
def call(conditions, variables, group)
-
condition, keys = [], group.split(', ')
-
if group_by_parent?
-
condition << "tca.container_id=?"
-
variables << keys.first.to_i
-
end
-
if group_by_submission?
-
condition << "requests.submission_id=?"
-
variables << keys.last.to_i
-
end
-
conditions << "(#{condition.join(" AND ")})"
-
end
-
1
private :call
-
-
1
def grouping
-
grouping = []
-
grouping << 'tca.container_id' if group_by_parent?
-
grouping << 'requests.submission_id' if group_by_submission?
-
grouping.join(',')
-
end
-
1
private :grouping
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
module Pipeline::InboxExtensions
-
1
def inbox(show_held_requests = true, current_page = 1, action = nil)
-
-
requests = proxy_association.scoped
-
pipeline = proxy_association.owner
-
# Build a list of methods to invoke to build the correct request list
-
actions = [ :unbatched ]
-
actions.concat(pipeline.custom_inbox_actions)
-
actions << ((pipeline.group_by_parent? or show_held_requests) ? :full_inbox : :pipeline_pending)
-
actions << [ (pipeline.group_by_parent? ? :holder_located : :located), pipeline.location_id ]
-
-
if action != :count
-
actions << :include_request_metadata
-
actions << (pipeline.group_by_submission? ? :ordered_for_submission_grouped_inbox : :ordered_for_ungrouped_inbox)
-
actions << pipeline.inbox_eager_loading
-
end
-
-
if action.present?
-
actions << [ action ]
-
elsif pipeline.paginate?
-
actions << [ :paginate, { :per_page => 50, :page => current_page } ]
-
end
-
-
actions.inject(requests) { |context, action| context.send(*Array(action)) }
-
end
-
-
# Used by the Pipeline class to retrieve the list of requests that are coming into the pipeline.
-
1
def inputs(show_held_requests = false)
-
ready_in_storage.send(show_held_requests ? :full_inbox : :pipeline_pending)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
# A pipeline that has its inbox grouped by submission needs to provide specific paging capabilities.
-
# All requests for a submission must appear on the same page, i.e. if a submission has 200 requests
-
# and there are only 100 allowed per page, then all of these requests must appear on a page and not
-
# across two.
-
1
module Pipeline::InboxGroupedBySubmission
-
1
def self.included(base)
-
6
base.has_many :inbox, :class_name => 'Request', :extend => [ Pipeline::RequestsInStorage, Pagination ]
-
end
-
-
# Always group by submission
-
1
def group_by_submission?
-
true
-
end
-
-
# This module overrides the behaviour of will_paginate so that the requests for an individual
-
# submission appear on the same page. It does this by first paging the submission IDs, and then
-
# limiting the query so that it only includes requests from these submissions.
-
1
module Pagination
-
1
def paginate(*args)
-
super(*args.push(args.extract_options!.merge(:finder => :paginated_by_submission, :total_entries => submissions(:count))))
-
end
-
-
1
def paginated_by_submission(*args)
-
options = args.extract_options!
-
options_for_submission_query = Hash[[ :limit, :offset ].map { |k| [ k, options.delete(k) ] if options.key?(k) }.compact]
-
all(options.deep_merge(:conditions => { :submission_id => submissions(:all, options_for_submission_query).map(&:submission_id) }))
-
end
-
-
#--
-
# Slight hack here in that we are assuming that only the submissions that have requests
-
# waiting in the correct location and in the right state are what we want to display
-
# for the inbox.
-
#++
-
1
def submissions(finder_method, options = {})
-
with_exclusive_scope do
-
ready_in_storage.full_inbox.send(finder_method, options.merge(:select => 'DISTINCT submission_id', :order => 'submission_id ASC'))
-
end
-
end
-
1
private :submissions
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Pipeline::InboxUngrouped
-
1
def self.included(base)
-
1
base.has_many :inbox, :class_name => 'Request', :extend => Pipeline::RequestsInStorage
-
end
-
-
# Never group by submission
-
1
def group_by_submission?
-
false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Pipeline::RequestsInStorage
-
1
def ready_in_storage
-
send((proxy_association.owner.group_by_parent ? :holder_located : :located), proxy_association.owner.location_id)
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module Pipeline::StockAssets
-
1
def can_create_stock_assets?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PipelineRequestInformationType < ActiveRecord::Base
-
1
belongs_to :pipeline
-
1
belongs_to :request_information_type
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
class Plate < Asset
-
include Api::PlateIO::Extensions
-
include ModelExtensions::Plate
-
include LocationAssociation::Locatable
-
include Transfer::Associations
-
include Transfer::State::PlateState
-
include PlatePurpose::Associations
-
include Barcode::Barcodeable
-
include Asset::Ownership::Owned
-
include Plate::Iterations
-
include Plate::FluidigmBehaviour
-
include SubmissionPool::Association::Plate
-
-
extend QcFile::Associations
-
has_qc_files
-
# The default state for a plate comes from the plate purpose
-
delegate :default_state, :to => :plate_purpose, :allow_nil => true
-
def state
-
plate_purpose.state_of(self)
-
end
-
-
def occupied_well_count
-
wells.with_contents.count
-
end
-
-
def summary_hash
-
{
-
:asset_id => id,
-
:barcode => { :ean13_barcode => ean13_barcode, :human_readable => sanger_human_barcode },
-
:occupied_wells => wells.with_aliquots.include_map.map(&:map_description)
-
}
-
end
-
-
def cherrypick_completed
-
plate_purpose.cherrypick_completed(self)
-
end
-
-
SAMPLE_PARTIAL = 'assets/samples_partials/plate_samples'
-
-
# The type of the barcode is delegated to the plate purpose because that governs the number of wells
-
delegate :barcode_type, :to => :plate_purpose, :allow_nil => true
-
delegate :asset_shape, :to => :plate_purpose, :allow_nil => true
-
delegate :supports_multiple_submissions?, :to => :plate_purpose
-
1
delegate :fluidigm_barcode, :to => :plate_metadata
-
delegate :dilution_factor,:dilution_factor=, :to => :plate_metadata
-
-
1
validates_length_of :fluidigm_barcode, :is => 10, :allow_blank => true
-
-
# Transfer requests into a plate are the requests leading into the wells of said plate.
-
1
def transfer_requests
-
wells.all(:include => :transfer_requests_as_target).map(&:transfer_requests_as_target).flatten
-
end
-
-
# About 10x faster than going through the wells
-
1
def submission_ids
-
@siat ||= container_associations.find(
-
:all,
-
:select => 'DISTINCT requests.submission_id',
-
:joins => 'LEFT JOIN requests ON requests.target_asset_id = container_associations.content_id',
-
:conditions => ['requests.submission_id IS NOT NULL AND requests.state NOT IN (?)',Request::Statemachine::INACTIVE]
-
).map(&:submission_id)
-
end
-
-
1
def submission_ids_as_source
-
@sias ||= container_associations.find(
-
:all,
-
:select => 'DISTINCT requests.submission_id',
-
:joins => 'LEFT JOIN requests ON requests.asset_id = container_associations.content_id',
-
:conditions => ['requests.submission_id IS NOT NULL AND requests.state NOT IN (?)',Request::Statemachine::INACTIVE]
-
).map(&:submission_id)
-
end
-
-
1
def all_submission_ids
-
submission_ids_as_source.present? ?
-
submission_ids_as_source :
-
submission_ids
-
end
-
-
1
def self.derived_classes
-
[ self, *self.descendants ].map(&:name)
-
end
-
-
1
def prefix
-
self.barcode_prefix.try(:prefix) || self.class.prefix
-
end
-
-
1
def submissions
-
s = Submission.find(:all,
-
:select => 'DISTINCT submissions.*',
-
:joins => [
-
'INNER JOIN requests as reqp ON reqp.submission_id = submissions.id',
-
'INNER JOIN container_associations AS caplp ON caplp.content_id = reqp.asset_id'
-
],
-
:conditions => ['caplp.container_id = ?',self.id]
-
)
-
return s unless s.blank?
-
Submission.find(:all,
-
:select => 'DISTINCT submissions.*',
-
:joins => [
-
'INNER JOIN requests as reqp ON reqp.submission_id = submissions.id',
-
'INNER JOIN container_associations AS caplp ON caplp.content_id = reqp.target_asset_id'
-
],
-
:conditions => ['caplp.container_id = ?',self.id]
-
)
-
end
-
-
1
def barcode_dilution_factor_created_at_hash
-
return {} if barcode.blank?
-
{
-
:barcode => generate_machine_barcode,
-
:dilution_factor => dilution_factor.to_s,
-
:created_at => created_at
-
}
-
end
-
-
-
1
class CommentsProxy
-
-
1
attr_reader :plate
-
-
1
def initialize(plate)
-
@plate=plate
-
end
-
-
1
def comment_assn
-
@asn||=Comment.for_plate(plate)
-
end
-
-
1
def method_missing(method,*args)
-
comment_assn.send(method,*args)
-
end
-
-
##
-
# We add the comments to each submission to ensure that are available for all the requests.
-
# At time of writing, submissions add comments to each request, so there are a lot of comments
-
# getting created here. (The intent is to change this so requests are treated similarly to plates)
-
1
def create!(options)
-
plate.submissions.each {|s| s.add_comment(options[:description],options[:user]) }
-
Comment.create!(options.merge(:commentable=>plate))
-
end
-
-
1
def create(options)
-
plate.submissions.each {|s| s.add_comment(options[:description],options[:user]) }
-
Comment.create(options.merge(:commentable=>plate))
-
end
-
-
# By default rails treats sizes for grouped queries different to sizes
-
# for ungrouped queries. Unfortunately plates could end up performing either.
-
# Grouped return a hash, for which we want the length
-
# otherwise we get an integer
-
# We need to urgently revisit this, as this solution is horrible.
-
1
def size(*args)
-
s = super
-
return s.length if s.respond_to?(:length)
-
s
-
end
-
1
def count(*args)
-
s = super
-
return s.length if s.respond_to?(:length)
-
s
-
end
-
-
end
-
-
1
def comments
-
@comments||=CommentsProxy.new(self)
-
end
-
-
1
def priority
-
Submission.find(:first,
-
:select => 'MAX(submissions.priority) AS priority',
-
:joins => [
-
'INNER JOIN requests as reqp ON reqp.submission_id = submissions.id',
-
'INNER JOIN container_associations AS caplp ON caplp.content_id = reqp.asset_id'
-
],
-
:conditions => ['caplp.container_id = ?',self.id]
-
).try(:priority)||Submission.find(:first,
-
:select => 'MAX(submissions.priority) AS priority',
-
:joins => [
-
'INNER JOIN requests as reqp ON reqp.submission_id = submissions.id',
-
'INNER JOIN container_associations AS caplp ON caplp.content_id = reqp.target_asset_id'
-
],
-
:conditions => ['caplp.container_id = ?',self.id]
-
).try(:priority)||0
-
end
-
-
1
def study
-
wells.first.try(:study)
-
end
-
-
1
def studies
-
Study.find_by_sql([ %Q{
-
SELECT DISTINCT s.*
-
FROM container_associations c
-
INNER JOIN aliquots a ON a.receptacle_id=c.content_id
-
INNER JOIN studies s ON a.study_id=s.id
-
WHERE c.container_id=?
-
}, self.id ])
-
end
-
-
1
contains :wells do #, :order => '`assets`.map_id ASC' do
-
-
# After importing wells we need to also create the AssetLink and WellAttribute information for them.
-
1
def post_import(links_data)
-
time_now = Time.now
-
links_data.each do |c|
-
AssetLink.create!(
-
:direct => true,
-
#:count => 1, Huh?
-
:ancestor_id => c.first,
-
:descendant_id => c.last
-
)
-
WellAttribute.create!(
-
:well_id => c.last,
-
:created_at => time_now,
-
:updated_at => time_now
-
)
-
end
-
end
-
1
private :post_import
-
-
1
def post_connect(well)
-
# AssetLink.create!(:ancestor => proxy_association.owner, :descendant => well)
-
end
-
1
private :post_connect
-
-
1
def construct!
-
Map.where_plate_size(proxy_association.owner.size).where_plate_shape(proxy_association.owner.asset_shape).in_row_major_order.map do |location|
-
build(:map => location)
-
end.tap do |wells|
-
proxy_association.owner.save!
-
AssetLink::Job.create(proxy_association.owner, wells)
-
end
-
end
-
-
1
def map_from_locations
-
{}.tap do |location_to_well|
-
self.walk_in_column_major_order do |well, _|
-
raise "Duplicated well at #{well.map.description}" if location_to_well.key?(well.map)
-
location_to_well[well.map] = well
-
end
-
end
-
end
-
-
# Returns the wells with their pool identifier included
-
1
def with_pool_id
-
proxy_association.owner.plate_purpose.pool_wells(self)
-
end
-
-
# Yields each pool and the wells that are in it
-
1
def walk_in_pools(&block)
-
with_pool_id.group_by(&:pool_id).each(&block)
-
end
-
-
# Walks the wells A1, B1, C1, ... A2, B2, C2, ... H12
-
1
def walk_in_column_major_order(&block)
-
self.in_column_major_order.each { |well| yield(well, well.map.column_order) }
-
end
-
-
# Walks the wells A1, A2, ... B1, B2, ... H12
-
1
def walk_in_row_major_order(&block)
-
self.in_row_major_order.each { |well| yield(well, well.map.row_order) }
-
end
-
-
1
def in_preferred_order
-
proxy_association.owner.plate_purpose.in_preferred_order(self)
-
end
-
end
-
-
1
scope :include_wells_and_attributes, -> { includes(:wells => [ :map, :well_attribute ]) }
-
-
#has_many :wells, :as => :holder, :class_name => "Well"
-
1
DEFAULT_SIZE = 96
-
1
self.prefix = "DN"
-
-
1
self.per_page = 50
-
-
1
before_create :set_plate_name_and_size
-
-
# scope :qc_started_plates, -> {
-
# {
-
# :select => "distinct assets.*",
-
# :order => 'assets.id DESC',
-
# :conditions => ["(events.family = 'create_dilution_plate_purpose' OR asset_audits.key = 'slf_receive_plates') AND plate_purpose_id = ?", PlatePurpose.find_by_name('Stock Plate') ],
-
# :joins => "LEFT OUTER JOIN `events` ON events.eventful_id = assets.id LEFT OUTER JOIN `asset_audits` ON asset_audits.asset_id = assets.id" ,
-
# :include => [:events, :asset_audits]
-
# }
-
# }
-
1
scope :qc_started_plates, -> {
-
select('DISTINCT assets.*').
-
joins("LEFT OUTER JOIN `events` ON events.eventful_id = assets.id LEFT OUTER JOIN `asset_audits` ON asset_audits.asset_id = assets.id").
-
where(["(events.family = 'create_dilution_plate_purpose' OR asset_audits.key = 'slf_receive_plates') AND plate_purpose_id = ?", PlatePurpose.stock_plate_purpose.id ]).
-
order('assets.id DESC').
-
includes(:events,:asset_audits)
-
}
-
-
-
1
scope :with_sample, ->(sample) {
-
{
-
:select => "distinct assets.*",
-
:joins => "LEFT OUTER JOIN container_associations AS wscas ON wscas.container_id = assets.id
-
LEFT JOIN assets AS wswells ON wswells.id = content_id
-
LEFT JOIN aliquots AS wsaliquots ON wsaliquots.receptacle_id = wswells.id",
-
:conditions => ["wsaliquots.sample_id IN(?)", Array(sample)]
-
}
-
}
-
-
1
scope :with_requests, ->(requests) {
-
{
-
:select => "DISTINCT assets.*",
-
:joins => [
-
"INNER JOIN container_associations AS wrca ON wrca.container_id = assets.id",
-
"INNER JOIN requests AS wrr ON wrr.asset_id = wrca.content_id"
-
],
-
:conditions => [
-
'wrr.id IN (?)',
-
requests.map(&:id)
-
]
-
}
-
}
-
-
1
scope :with_wells, ->(wells) {
-
select('DISTINCT assets.*').
-
joins(:container_associations).
-
where(:container_associations=>{:content_id=> wells.map(&:id) })
-
}
-
-
1
def wells_sorted_by_map_id
-
wells.sorted
-
end
-
-
1
def wells_sorted_by(&block)
-
wells.sort { |a, b| block.call(a) <=> block.call(b) }
-
end
-
-
1
def children_and_holded
-
( children | wells )
-
end
-
-
1
def find_map_by_rowcol(row, col)
-
# Count from 0
-
description = asset_shape.location_from_row_and_column(row,col+1,size)
-
Map.where(
-
:description => description,
-
:asset_size => size,
-
:asset_shape_id => asset_shape
-
).first
-
end
-
-
1
def find_well_by_rowcol(row, col)
-
map = self.find_map_by_rowcol(row,col)
-
return nil if map.nil?
-
self.find_well_by_name(map.description)
-
end
-
-
1
def add_well_holder(well)
-
children << well
-
well.plate = self
-
end
-
-
1
def add_well(well, row=nil, col=nil)
-
add_well_holder(well)
-
if row
-
well.map = find_map_by_rowcol(row, col)
-
end
-
end
-
-
1
def add_well_by_map_description(well,map_description)
-
add_well_holder(well)
-
well.map = Map.find_by_description_and_asset_size(map_description,size)
-
well.save!
-
end
-
-
1
def add_and_save_well(well, row=nil, col=nil)
-
self.add_well(well, row, col)
-
well.save!
-
end
-
-
1
def find_well_by_name(well_name)
-
self.wells.located_at_position(well_name).first
-
end
-
1
alias :find_well_by_map_description :find_well_by_name
-
-
1
def plate_header
-
[""] + plate_columns
-
end
-
-
1
def plate_rows
-
("A".."#{(?A.getbyte(0)+height-1).chr}").to_a
-
end
-
-
1
def plate_columns
-
(1..width).to_a
-
end
-
-
1
def get_plate_type
-
if self.descriptor_value('Plate Type').nil?
-
plate_type = self.get_external_value('plate_type_description')
-
set_plate_type(plate_type)
-
end
-
self.descriptor_value('Plate Type')
-
end
-
-
1
def set_plate_type(result)
-
self.add_descriptor(Descriptor.new({:name => "Plate Type", :value => result}))
-
self.save
-
end
-
-
1
def stock_plate_name
-
(self.get_plate_type == "Stock Plate" || self.get_plate_type.blank?) ? PlatePurpose.cherrypickable_as_source.first.name : self.get_plate_type
-
end
-
-
1
def details
-
purpose.try(:name)||'Unknown plate purpose'
-
end
-
-
1
def control_well_exists?
-
Request.into_by_id(wells.map(&:id)).any? do |request|
-
request.asset.plate.is_a?(ControlPlate)
-
end
-
end
-
-
# A plate has a sample with the specified name if any of its wells have that sample.
-
1
def sample?(sample_name)
-
self.wells.any? do |well|
-
well.aliquots.any? { |aliquot| aliquot.sample.name == sample_name }
-
end
-
end
-
-
1
def storage_location
-
@storage_location ||= obtain_storage_location
-
end
-
-
1
def storage_location_service
-
@storage_location_service
-
end
-
-
1
def obtain_storage_location
-
# From LabWhere
-
info_from_labwhere = LabWhereClient::Labware.find_by_barcode(ean13_barcode)
-
unless info_from_labwhere.nil? || info_from_labwhere.location.nil?
-
@storage_location_service = 'LabWhere'
-
return info_from_labwhere.location.location_info
-
end
-
-
# From ETS
-
@storage_location_service = 'ETS'
-
return "Control" if self.is_a?(ControlPlate)
-
return "" if self.barcode.blank?
-
return ['storage_area', 'storage_device', 'building_area', 'building'].map do |key|
-
self.get_external_value(key)
-
end.compact.join(' - ')
-
-
rescue LabWhereClient::LabwhereException => e
-
@storage_location_service = 'None'
-
return "Not found (#{e.message})"
-
end
-
-
1
def barcode_for_tecan
-
raise StandardError, 'Purpose is not valid' if plate_purpose.present? and not plate_purpose.valid?
-
plate_purpose.present? ? send(:"#{plate_purpose.barcode_for_tecan}") : ean13_barcode
-
end
-
-
1
def infinium_barcode
-
self.plate_metadata.infinium_barcode
-
end
-
-
1
def infinium_barcode=(barcode)
-
self.plate_metadata.infinium_barcode = barcode
-
self.plate_metadata.save!
-
end
-
-
1
def valid_infinium_barcode?(barcode)
-
true
-
end
-
-
1
def self.create_from_rack_csv(file_location, plate_barcode)
-
plate = self.create(:name => "Plate #{plate_barcode}", :barcode => plate_barcode, :size => 96)
-
-
CSV.foreach(file_location) do |row|
-
map = Map.find_for_cell_location(row.first, plate.size)
-
unless row.last.strip.blank?
-
asset = Asset.find_by_two_dimensional_barcode(row.last.strip)
-
unless asset.nil?
-
well = plate.wells.create(:sample => asset.sample, :map_id => map.id)
-
well.name = "#{asset} #{well.id}"
-
well.save
-
AssetLink.create_edge(asset, well)
-
else
-
well = plate.wells.create(:map_id => map.id)
-
end
-
else
-
well = plate.wells.create(:map_id => map.id)
-
end
-
end
-
plate
-
end
-
-
1
def submission_time(current_time)
-
current_time.strftime("%Y-%m-%dT%H_%M_%SZ")
-
end
-
-
1
def self.create_plates_with_barcodes(params)
-
begin
-
params[:snp_plates].each do |index,plate_barcode_id|
-
next if plate_barcode_id.blank?
-
plate = Plate.create(:barcode => "#{plate_barcode_id}", :name => "Plate #{plate_barcode_id}", :size => DEFAULT_SIZE)
-
storage_location = Location.find(params[:asset][:location_id])
-
plate.location = storage_location
-
plate.save!
-
end
-
rescue
-
return false
-
end
-
-
true
-
end
-
-
1
def self.plate_ids_from_requests(requests)
-
plate_ids = []
-
requests.map do |request|
-
next if request.asset.nil?
-
next unless request.asset.is_a?(Well)
-
next if request.asset.plate.nil?
-
plate_ids << request.asset.plate.id
-
end
-
-
plate_ids.uniq
-
end
-
-
1
def plate_asset_group_name(current_time)
-
if self.barcode
-
self.barcode+"_asset_group_#{submission_time(current_time)}"
-
else
-
self.id+"_asset_group_#{submission_time(current_time)}"
-
end
-
end
-
-
1
def create_plate_submission(project, study, user, current_time)
-
LinearSubmission.build!(
-
:study => study,
-
:project => project,
-
:workflow => genotyping_submission_workflow,
-
:user => user,
-
:assets => wells,
-
:request_types => submission_workflow_request_type_ids(genotyping_submission_workflow)
-
)
-
end
-
-
1
def submission_workflow_request_type_ids(submission_workflow)
-
submission_workflow.request_types.map(&:id)
-
end
-
-
1
def genotyping_submission_workflow
-
Submission::Workflow.find_by_key("microarray_genotyping")
-
end
-
-
1
def self.create_plates_submission(project, study, plates, user)
-
return false if user.nil? || project.nil? || study.nil?
-
current_time = Time.now
-
-
project.save
-
plates.each do |plate|
-
plate.generate_plate_submission(project, study, user,current_time)
-
end
-
-
true
-
end
-
-
# Should return true if any samples on the plate contains gender information
-
1
def contains_gendered_samples?
-
wells.any? do |well|
-
well.aliquots.any? { |aliquot| aliquot.sample.present? and not aliquot.sample.sample_metadata.gender.blank? }
-
end
-
end
-
-
1
def generate_plate_submission(project, study, user, current_time)
-
submission = self.create_plate_submission(project, study, user, current_time)
-
if submission
-
self.events.create!(:message => I18n.t('studies.submissions.plate.event.success', :barcode => self.barcode, :submission_id => submission.id), :created_by => user.login)
-
else
-
self.events.create!(:message => I18n.t('studies.submissions.plate.event.failed', :barcode => self.barcode), :created_by => user.login)
-
study.errors.add("plate_barcode", "Couldnt create submission for plate #{plate_barcode}")
-
end
-
end
-
-
1
def create_sample_tubes
-
wells.map(&:create_child_sample_tube)
-
end
-
-
1
def create_sample_tubes_and_print_barcodes(barcode_printer,location = nil)
-
sample_tubes = create_sample_tubes
-
Asset.print_assets(sample_tubes, barcode_printer)
-
if location
-
location.set_locations(sample_tubes)
-
end
-
-
sample_tubes
-
end
-
-
1
def self.create_sample_tubes_asset_group_and_print_barcodes(plates, barcode_printer, location, study)
-
return nil if plates.empty?
-
plate_barcodes = plates.map{ |plate| plate.barcode}
-
asset_group = AssetGroup.find_or_create_asset_group("#{plate_barcodes.join('-')} #{Time.now.to_formatted_s(:sortable)} ", study)
-
plates.each do |plate|
-
next if plate.wells.empty?
-
asset_group.assets << plate.create_sample_tubes_and_print_barcodes(barcode_printer, location)
-
end
-
-
return nil if asset_group.assets.empty?
-
asset_group.save!
-
-
asset_group
-
end
-
-
1
def stock_plate?
-
return true if self.plate_purpose.nil?
-
self.plate_purpose.can_be_considered_a_stock_plate? && self.plate_purpose.attatched?(self)
-
end
-
-
1
def stock_plate
-
@stock_plate ||= stock_plate? ? self : lookup_stock_plate
-
end
-
-
1
def lookup_stock_plate
-
spp = PlatePurpose.where(:can_be_considered_a_stock_plate=>true).all.map(&:id)
-
self.ancestors.order('created_at DESC').where(:plate_purpose_id=>spp).first
-
end
-
1
private :lookup_stock_plate
-
-
1
def original_stock_plates
-
ancestors.find(:all,:conditions => {:plate_purpose_id => PlatePurpose.stock_plate_purpose })
-
end
-
-
1
def ancestor_of_purpose(ancestor_purpose_id)
-
return self if self.plate_purpose_id == ancestor_purpose_id
-
self.ancestors.order('created_at DESC').where(:plate_purpose_id=>ancestor_purpose_id).first
-
end
-
-
1
def ancestors_of_purpose(ancestor_purpose_id)
-
return [self] if self.plate_purpose_id == ancestor_purpose_id
-
ancestors.find(:all,:order => 'created_at DESC', :conditions => {:plate_purpose_id=>ancestor_purpose_id})
-
end
-
-
1
def child_dilution_plates_filtered_by_type(parent_model)
-
self.children.select{ |p| p.is_a?(parent_model) }
-
end
-
-
1
def children_of_dilution_plates(parent_model, child_model)
-
child_dilution_plates_filtered_by_type(parent_model).map{ |dilution_plate| dilution_plate.children.select{ |p| p.is_a?(child_model) } }.flatten.select{ |p| ! p.nil? }
-
end
-
-
1
def child_pico_assay_plates
-
children_of_dilution_plates(PicoDilutionPlate, PicoAssayAPlate)
-
end
-
-
1
def child_gel_dilution_plates
-
children_of_dilution_plates(WorkingDilutionPlate, GelDilutionPlate)
-
end
-
-
1
def child_sequenom_qc_plates
-
children_of_dilution_plates(WorkingDilutionPlate, SequenomQcPlate)
-
end
-
-
1
def find_study_abbreviation_from_parent
-
self.parent.try(:wells).try(:first).try(:study).try(:abbreviation)
-
end
-
-
1
def self.create_with_barcode!(*args, &block)
-
attributes = args.extract_options!
-
barcode = args.first || attributes[:barcode]
-
# If this gets called on plate_purpose.plates it implicitly scopes
-
# plate to the plate purpose of choice.
-
barcode = nil if barcode.present? and unscoped.find_by_barcode(barcode).present?
-
barcode ||= PlateBarcode.create.barcode
-
create!(attributes.merge(:barcode => barcode), &block)
-
end
-
-
1
def self.plates_from_scanned_plate_barcodes(source_plate_barcodes)
-
source_plate_barcodes.scan(/\d+/).map { |barcode| find_from_machine_barcode(barcode) }
-
end
-
-
#--
-
# NOTE: I'm getting odd behaviour where '&method(:find_from_machine_barcode)' raises a SecurityError. I haven't
-
# been able to track down why, and it only happens under 'rake cucumber', so somewhere something is doing something
-
# nasty.
-
#++
-
1
def self.plates_from_scanned_plates_and_typed_plate_ids(source_plate_barcodes)
-
scanned_plates = source_plate_barcodes.scan(/\d+/).map { |v| find_from_machine_barcode(v) }
-
typed_plates = source_plate_barcodes.scan(/\d+/).map { |v| find_by_barcode(v) }
-
-
(scanned_plates | typed_plates).compact
-
end
-
-
1
def number_of_blank_samples
-
self.wells.with_blank_samples.count
-
end
-
-
1
def default_plate_size
-
DEFAULT_SIZE
-
end
-
-
1
def scored?
-
wells.any? { |w| w.get_gel_pass }
-
end
-
-
1
def buffer_required?
-
wells.any?(&:buffer_required?)
-
end
-
-
1
def valid_positions?(positions)
-
unique_positions_on_plate, unique_positions_from_caller = Map.where_description(positions).where_plate_size(self.size).where_plate_shape(self.asset_shape).all.map(&:description).sort.uniq, positions.sort.uniq
-
unique_positions_on_plate == unique_positions_from_caller
-
end
-
-
1
def name_for_label
-
self.name
-
end
-
-
1
def set_plate_name_and_size
-
self.name = "Plate #{barcode}" if self.name.blank?
-
self.size = default_plate_size if self.size.nil?
-
self.location = Location.find_by_name("Sample logistics freezer") if self.location_id.nil?
-
end
-
1
private :set_plate_name_and_size
-
-
1
extend Metadata
-
1
has_metadata do
-
1
attribute(:infinium_barcode)
-
1
attribute(:fluidigm_barcode)
-
end
-
-
1
def barcode_label_for_printing
-
PrintBarcode::Label.new(
-
:number => self.barcode,
-
:study => self.find_study_abbreviation_from_parent,
-
:suffix => self.parent.try(:barcode),
-
:prefix => self.barcode_prefix.prefix
-
)
-
end
-
-
1
def height
-
asset_shape.plate_height(size)
-
end
-
-
1
def width
-
asset_shape.plate_width(size)
-
end
-
-
# This method returns a map from the wells on the plate to their stock well.
-
1
def stock_wells
-
# Optimisation: if the plate is a stock plate then it's wells are it's stock wells!
-
return Hash[wells.with_pool_id.map { |w| [w,[w]] }] if stock_plate?
-
Hash[wells.include_stock_wells.with_pool_id.map { |w| [w, w.stock_wells.sort_by {|sw| sw.map.column_order } ] }.reject { |_,v| v.empty? }].tap do |stock_wells_hash|
-
raise "No stock plate associated with #{id}" if stock_wells_hash.empty?
-
end
-
end
-
-
1
def convert_to(new_purpose)
-
self.update_attributes!(:plate_purpose=>new_purpose)
-
end
-
-
1
def compatible_purposes
-
PlatePurpose.compatible_with_purpose(self.purpose)
-
end
-
-
1
def update_concentrations_from(parser)
-
ActiveRecord::Base.transaction do
-
parser.each_well_and_parameters do |position,concentration,molarity|
-
wells.include_map.detect {|w| w.map_description == position }.tap do |well|
-
well.set_concentration(concentration)
-
well.set_molarity(molarity)
-
well.save!
-
end
-
end
-
end
-
true
-
end
-
-
1
def orders_as_target
-
Order.with_plate_as_target(self)
-
end
-
-
1
def samples_in_order(order_id)
-
Sample.for_plate_and_order(self.id,order_id)
-
end
-
-
1
def samples_in_order_by_target(order_id)
-
Sample.for_plate_and_order_as_target(self.id,order_id)
-
end
-
-
1
def contained_samples
-
Sample.on_plate(self)
-
end
-
-
1
def team
-
ProductLine.find(:first,
-
:joins => [
-
'INNER JOIN request_types ON request_types.product_line_id = product_lines.id',
-
'INNER JOIN requests ON requests.request_type_id = request_types.id',
-
'INNER JOIN well_links ON well_links.source_well_id = requests.asset_id AND well_links.type = "stock"',
-
'INNER JOIN container_associations AS ca ON ca.content_id = well_links.target_well_id'
-
],
-
:conditions => ['ca.container_id = ?',self.id]).try(:name)||'UNKNOWN'
-
end
-
-
# Barcode is stored as a string, jet in a number of places is treated as
-
# a number. If we conver it before searching, things are faster!
-
1
def find_by_barcode(barcode)
-
super(barcode.to_s)
-
end
-
-
1
alias_method :friendly_name, :sanger_human_barcode
-
1
def subject_type
-
'plate'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
-
1
class Plate::Creator < ActiveRecord::Base
-
-
1
PlateCreationError = Class.new(StandardError)
-
-
1
class PurposeRelationship < ActiveRecord::Base
-
1
self.table_name =('plate_creator_purposes')
-
-
1
belongs_to :plate_purpose
-
1
belongs_to :plate_creator, :class_name => 'Plate::Creator'
-
-
end
-
-
1
class ParentPurposeRelationship < ActiveRecord::Base
-
1
self.table_name=('plate_creator_parent_purposes')
-
-
1
belongs_to :plate_purpose, :class_name => 'Purpose'
-
end
-
-
1
self.table_name = 'plate_creators'
-
-
# These are the plate purposes that will be created when this creator is used.
-
1
has_many :plate_creator_purposes, :class_name => 'Plate::Creator::PurposeRelationship', :dependent => :destroy, :foreign_key => :plate_creator_id
-
1
has_many :plate_purposes, :through => :plate_creator_purposes
-
-
1
has_many :parent_purpose_relationships, :class_name => 'Plate::Creator::ParentPurposeRelationship',:dependent => :destroy, :foreign_key => :plate_creator_id
-
1
has_many :parent_plate_purposes, :through => :parent_purpose_relationships, :source => :plate_purpose
-
-
# If there are no barcodes supplied then we use the plate purpose we represent
-
1
belongs_to :plate_purpose
-
-
1
serialize :valid_options
-
-
1
def can_create_plates?(source_plate, plate_purposes)
-
parent_plate_purposes.empty? || parent_plate_purposes.include?(source_plate.purpose)
-
end
-
-
# Executes the plate creation so that the appropriate child plates are built.
-
1
def execute(source_plate_barcodes, barcode_printer, scanned_user, creator_parameters=nil)
-
ActiveRecord::Base.transaction do
-
new_plates = create_plates(source_plate_barcodes, scanned_user, creator_parameters)
-
return false if new_plates.empty?
-
new_plates.group_by(&:plate_purpose).each do |plate_purpose, plates|
-
barcode_printer.print_labels(plates.map(&:barcode_label_for_printing), Plate.prefix, "long", plate_purpose.name.to_s, scanned_user.login)
-
end
-
true
-
end
-
end
-
-
1
def create_plate_without_parent(creator_parameters)
-
plate = self.plate_purpose.plates.create_with_barcode!
-
-
creator_parameters.set_plate_parameters(plate) unless creator_parameters.nil?
-
-
return [ plate ]
-
end
-
-
1
def create_plates(source_plate_barcodes, current_user, creator_parameters=nil)
-
return create_plate_without_parent(creator_parameters) if source_plate_barcodes.blank?
-
-
scanned_barcodes = source_plate_barcodes.scan(/\d+/)
-
raise PlateCreationError, "Scanned plate barcodes in incorrect format: #{source_plate_barcodes.inspect}" if scanned_barcodes.blank?
-
-
# NOTE: Plate barcodes are not unique within certain laboratories. That means that we cannot do:
-
# plates = Plate.with_machine_barcode(*scanned_barcodes).all(:include => [ :location, { :wells => :aliquots } ])
-
# Because then you get multiple matches. So we take the first match, which is just not right.
-
scanned_barcodes.map do |scanned|
-
plate =
-
Plate.with_machine_barcode(scanned).first(:include => [ :location, { :wells => :aliquots } ]) or
-
raise ActiveRecord::RecordNotFound, "Could not find plate with machine barcode #{scanned.inspect}"
-
unless can_create_plates?(plate, plate_purposes)
-
raise PlateCreationError, "Scanned plate #{scanned} has a purpose #{plate.purpose.name} not valid for creating [#{self.plate_purposes.map(&:name).join(',')}]"
-
end
-
create_child_plates_from(plate, current_user, creator_parameters)
-
end.flatten
-
end
-
1
private :create_plates
-
-
1
def create_child_plates_from(plate, current_user,creator_parameters)
-
stock_well_picker = plate.plate_purpose.can_be_considered_a_stock_plate? ? ->(w) { [w] } : ->(w) { w.stock_wells }
-
plate_purposes.map do |target_plate_purpose|
-
target_plate_purpose.target_plate_type.constantize.create_with_barcode!(plate.barcode) do |child_plate|
-
child_plate.plate_purpose = target_plate_purpose
-
child_plate.size = plate.size
-
child_plate.location = plate.location
-
child_plate.name = "#{target_plate_purpose.name} #{child_plate.barcode}"
-
end.tap do |child_plate|
-
child_plate.wells << plate.wells.map do |well|
-
well.dup.tap do |child_well|
-
child_well.aliquots = well.aliquots.map(&:dup)
-
child_well.stock_wells.attach(stock_well_picker.call(well))
-
end
-
end
-
-
creator_parameters.set_plate_parameters(child_plate, plate) unless creator_parameters.nil?
-
-
AssetLink.create_edge!(plate, child_plate)
-
plate.events.create_plate!(target_plate_purpose, child_plate, current_user)
-
end
-
end
-
end
-
1
private :create_child_plates_from
-
end
-
1
require 'bigdecimal'
-
1
require 'bigdecimal/util'
-
-
1
class Plate::CreatorParameters
-
1
def initialize(params_plate_creator)
-
@params = params_plate_creator
-
end
-
-
1
public
-
1
def set_plate_parameters(plate, parent_plate=nil)
-
# All the creation parameters are applied as String values into the ActiveRecord. Maybe in
-
# future this will need to be reviewed in case Ruby conversion from strings is not appropriate
-
plate.update_attributes!(plate_parameters(plate, parent_plate)) unless @params.nil?
-
end
-
-
1
def plate_dilution_factor(plate)
-
return plate.dilution_factor unless plate.nil?
-
# If nobody specify any dilution factor (not even the PlateCreator), I can't assume any
-
# default dilution factor. We'll fall back to database default value (if it has one)
-
nil
-
end
-
-
1
def params_has_dilution_factor?(params)
-
(!params[:dilution_factor].nil?) && (!params[:dilution_factor].to_s.empty?)
-
end
-
-
1
def plate_parameters(plate, parent_plate=nil)
-
params = @params.clone
-
-
parent_dilution_factor = plate_dilution_factor(parent_plate)
-
if params_has_dilution_factor?(params)
-
# The dilution factor of the parent is propagated to the children taking the parent's dilution
-
# as basis.
-
params[:dilution_factor] = (params[:dilution_factor].to_d * parent_dilution_factor) unless parent_dilution_factor.nil?
-
else
-
# If not specified, I'll inherit the value of the source plate (if it has one)
-
params[:dilution_factor] = parent_dilution_factor
-
end
-
# If I don't have a dilution factor yet, I'll let the value fall back to database default
-
params.delete(:dilution_factor) if params[:dilution_factor].nil?
-
-
# Remove any symbol not valid for plate creation (just dilution factor at now)
-
params.delete_if{|k,v| k.to_sym != :dilution_factor}
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
module Plate::FluidigmBehaviour
-
-
1
class FluidigmError < StandardError; end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
-
1
scope :requiring_fluidigm_data, -> {
-
fluidigm_request_id = RequestType.find_by_key!('pick_to_fluidigm').id
-
-
select('DISTINCT assets.*, plate_metadata.fluidigm_barcode AS fluidigm_barcode').
-
joins([
-
'INNER JOIN plate_metadata ON plate_metadata.plate_id = assets.id AND plate_metadata.fluidigm_barcode IS NOT NULL', # The fluidigm metadata
-
'INNER JOIN container_associations AS fluidigm_plate_association ON fluidigm_plate_association.container_id = assets.id', # The fluidigm wells
-
"INNER JOIN requests ON requests.target_asset_id = fluidigm_plate_association.content_id AND state = \'passed\' AND requests.request_type_id = #{fluidigm_request_id}", # Link to their requests
-
-
'INNER JOIN well_links AS stock_well_link ON stock_well_link.target_well_id = fluidigm_plate_association.content_id AND type= \'stock\'',
-
'LEFT OUTER JOIN events ON eventful_id = assets.id AND eventful_type = "Asset" AND family = "update_fluidigm_plate" AND content = "FLUIDIGM_DATA" '
-
]).
-
where('events.id IS NULL')
-
}
-
-
end
-
end
-
-
1
def retrieve_fluidigm_data
-
ActiveRecord::Base.transaction do
-
fluidigm_data = FluidigmFile::Finder.find(fluidigm_barcode)
-
return false if fluidigm_data.empty? # Return false if we have no data
-
apply_fluidigm_data(FluidigmFile.new(fluidigm_data.content))
-
return true
-
end
-
end
-
-
1
def apply_fluidigm_data(fluidigm_file)
-
raise FluidigmError, "File does not match plate" unless fluidigm_file.for_plate?(fluidigm_barcode)
-
-
wells.located_at(fluidigm_file.well_locations).include_stock_wells.each do |well|
-
well.stock_wells.each do |sw|
-
sw.update_gender_markers!( fluidigm_file.well_at(well.map_description).gender_markers,'FLUIDIGM' )
-
sw.update_sequenom_count!( fluidigm_file.well_at(well.map_description).count,'FLUIDIGM' )
-
end
-
end
-
self.events.updated_fluidigm_plate!('FLUIDIGM_DATA')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module Plate::Iterations
-
-
1
def iteration
-
return nil if parent.nil? # No parent means no iteration, not a 0 iteration.
-
-
# NOTE: This is how to do row numbering with MySQL! It essentially joins the assets and asset_links
-
# tables to find all of the child plates of our parent that have the same plate purpose, numbering
-
# those rows to give the iteration number for each plate.
-
iteration_of_plate = connection.select_one(%Q{
-
SELECT iteration
-
FROM (
-
SELECT iteration_plates.id, @rownum:=@rownum+1 AS iteration
-
FROM (
-
SELECT assets.id
-
FROM asset_links
-
JOIN assets ON asset_links.descendant_id=assets.id
-
WHERE asset_links.direct=TRUE AND ancestor_id=#{parent.id} AND assets.sti_type in (#{Plate.derived_classes.map(&:inspect).join(',')}) AND assets.plate_purpose_id=#{plate_purpose.id}
-
ORDER by assets.created_at ASC
-
) AS iteration_plates,
-
(SELECT @rownum:=0) AS r
-
) AS a
-
WHERE a.id=#{self.id}
-
}, "Plate #{self.id} iteration query")
-
-
iteration_of_plate['iteration'].to_i
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
class PlateBarcode < ActiveResource::Base
-
1
self.site = configatron.plate_barcode_service
-
1
self.format = ActiveResource::Formats::XmlFormat
-
-
1
if Rails.env == 'development'
-
def self.create
-
if @barcode.nil?
-
@barcode = Asset.first(
-
:conditions => 'barcode is not null and barcode!="9999999" and length(barcode)=7',
-
:order => 'barcode desc'
-
).try(:barcode).to_i
-
-
@barcode = 9000000 if @barcode.zero?
-
end
-
-
OpenStruct.new(:barcode => (@barcode += 1))
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
# Creating an instance of this class causes the target to become converted to the new
-
# plate purpose
-
1
class PlateConversion < ActiveRecord::Base
-
-
1
include Uuid::Uuidable
-
-
1
belongs_to :target, :class_name => 'Plate'
-
1
belongs_to :user
-
1
belongs_to :purpose, :class_name => 'PlatePurpose'
-
-
1
belongs_to :parent, :class_name => 'Plate'
-
-
1
validates :target, :presence => true
-
1
validates :purpose, :presence => true
-
1
validates :user, :presence =>true
-
-
1
after_create :convert_target
-
-
1
private
-
-
1
def convert_target
-
target.convert_to(purpose)
-
AssetLink.create!(:ancestor_id => parent.id, :descendant_id => target.id) if parent.present?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013 Genome Research Ltd.
-
# Creating an instance of this class causes a child plate, with the specified plate type, to be created from
-
# the parent.
-
1
class PlateCreation < AssetCreation
-
1
include_plate_named_scope :parent
-
-
# This is the child that is created from the parent. It cannot be assigned before validation.
-
1
belongs_to :parent, :class_name => 'Plate'
-
-
1
def record_creation_of_children
-
parent.events.create_plate!(child_purpose, child, user)
-
end
-
1
private :record_creation_of_children
-
-
1
module Children
-
-
1
def self.included(base)
-
2
base.class_eval %Q{
-
include_plate_named_scope :child
-
belongs_to :child, :class_name => 'Plate'
-
-
validates_unassigned(:child)
-
}
-
end
-
-
1
def target_for_ownership
-
child
-
end
-
1
private :target_for_ownership
-
-
1
def children
-
[self.child]
-
end
-
1
private :children
-
-
1
def create_children!
-
self.child = child_purpose.create!(:location=>parent.location)
-
end
-
1
private :create_children!
-
-
end
-
1
include Children
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
-
1
class PlateOwner < ActiveRecord::Base
-
-
1
belongs_to :user
-
1
belongs_to :plate
-
1
belongs_to :eventable, :polymorphic => true
-
-
1
validates_presence_of :eventable
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class PlatePurpose < Purpose
-
1
module Associations
-
1
def self.included(base)
-
1
base.class_eval do
-
# TODO: change to purpose_id
-
1
belongs_to :plate_purpose, :foreign_key => :plate_purpose_id
-
1
belongs_to :purpose, :foreign_key => :plate_purpose_id
-
1
scope :with_plate_purpose, ->(*purposes) {
-
{ :conditions => { :plate_purpose_id => purposes.flatten.map(&:id) } }
-
}
-
end
-
end
-
-
# Delegate the change of state to our plate purpose.
-
1
def transition_to(state, user, contents = nil, customer_accepts_responsibility=false)
-
purpose.transition_to(self, state, user, contents, customer_accepts_responsibility)
-
end
-
-
# Delegate the transfer request type determination to our plate purpose
-
1
def transfer_request_type_from(source)
-
purpose.transfer_request_type_from(source.plate_purpose)
-
end
-
end
-
-
1
include Relationship::Associations
-
-
# We declare the scopes as lambdas as Rails 3.2 seems to fail to include the various subclasses otherwise
-
1
scope :compatible_with_purpose, ->(purpose) {
-
purpose.nil? ?
-
where('FALSE') :
-
where(["(target_type is null and 'Plate'=?) or target_type=?",purpose.target_plate_type, purpose.target_plate_type]).
-
order("name ASC")
-
}
-
-
1
scope :cherrypickable_as_target, -> { where( :cherrypickable_target => true ) }
-
1
scope :cherrypickable_as_source, -> { where( :cherrypickable_source => true ) }
-
1
scope :cherrypickable_default_type, -> { where( :cherrypickable_target => true, :cherrypickable_source => true ) }
-
1
scope :for_submissions, -> { where('can_be_considered_a_stock_plate = true OR name = "Working Dilution"').
-
order('can_be_considered_a_stock_plate DESC') }
-
1
scope :considered_stock_plate, -> { where( :can_be_considered_a_stock_plate => true ) }
-
-
1
serialize :cherrypick_filters
-
1
validates_presence_of(:cherrypick_filters, :if => :cherrypickable_target?)
-
1
before_validation(:if => :cherrypickable_target?) do |r|
-
r[:cherrypick_filters] ||= [ 'Cherrypick::Strategy::Filter::ShortenPlexesToFit' ]
-
end
-
-
1
belongs_to :asset_shape, :class_name => 'AssetShape'
-
-
1
def source_plate(plate)
-
source_purpose_id.present? ? plate.ancestor_of_purpose(source_purpose_id) : plate.stock_plate
-
end
-
1
alias_method :library_source_plate, :source_plate
-
-
1
def source_plates(plate)
-
source_purpose_id.present? ? plate.ancestors_of_purpose(source_purpose_id) : [plate.stock_plate]
-
end
-
1
alias_method :library_source_plates, :source_plates
-
-
1
def cherrypick_strategy
-
Cherrypick::Strategy.new(self)
-
end
-
-
1
def cherrypick_dimension
-
cherrypick_direction == 'column' ? plate_height : plate_width
-
end
-
-
1
def cherrypick_completed(plate)
-
messenger_creators.each {|creator| creator.create!(plate) }
-
end
-
-
1
def plate_height
-
asset_shape.plate_height(size)
-
end
-
-
1
def plate_width
-
asset_shape.plate_width(size)
-
end
-
-
1
def cherrypick_filters
-
self[:cherrypick_filters].map(&:constantize)
-
end
-
-
# The state of a plate is based on the transfer requests.
-
1
def state_of(plate)
-
plate.send(:state_from, plate.transfer_requests)
-
end
-
-
# Updates the state of the specified plate to the specified state. The basic implementation does this by updating
-
# all of the TransferRequest instances to the state specified. If contents is blank then the change is assumed to
-
# relate to all wells of the plate, otherwise only the selected ones are updated.
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility=false)
-
wells = plate.wells
-
wells = wells.located_at(contents) unless contents.blank?
-
-
transition_state_requests(wells, state)
-
fail_stock_well_requests(wells,customer_accepts_responsibility) if state == 'failed'
-
end
-
-
-
1
module Overrideable
-
1
def transition_state_requests(wells, state)
-
wells = wells.all(:include => { :requests_as_target => { :asset => :aliquots, :target_asset => :aliquots } })
-
wells.each { |w| w.requests_as_target.map { |r| r.transition_to(state) } }
-
end
-
1
private :transition_state_requests
-
-
# Override this method to control the requests that should be failed for the given wells.
-
1
def fail_request_details_for(wells)
-
wells.each do |well|
-
submission_ids = well.requests_as_target.map(&:submission_id)
-
next if submission_ids.empty?
-
-
stock_wells = well.stock_wells.map(&:id)
-
next if stock_wells.empty?
-
-
yield(submission_ids, stock_wells)
-
end
-
end
-
1
private :fail_request_details_for
-
end
-
-
1
include Overrideable
-
-
1
def fail_stock_well_requests(wells,customer_accepts_responsibility)
-
# Load all of the requests that come from the stock wells that should be failed. Note that we can't simply change
-
# their state, we have to actually use the statemachine method to do this to get the correct behaviour.
-
conditions, parameters = [], []
-
fail_request_details_for(wells) do |submission_ids, stock_wells|
-
# Efficiency gain to be had using '=' over 'IN' when there is only one value to consider.
-
condition, args = [], []
-
condition[0], args[0] = (submission_ids.size == 1) ? ['submission_id=?',submission_ids.first] : ['submission_id IN (?)',submission_ids]
-
condition[1], args[1] = (stock_wells.size == 1) ? ['asset_id=?',stock_wells.first] : ['asset_id IN (?)',stock_wells]
-
conditions << "(#{condition[0]} AND #{condition[1]})"
-
parameters.concat(args)
-
end
-
raise "Apparently there are not requests on these wells?" if conditions.empty?
-
Request.where_is_not_a?(TransferRequest).all(:conditions => [ "(#{conditions.join(' OR ')})", *parameters ]).map do |request|
-
# This can probably be switched for an each, as I don't think the array is actually used for anything.
-
request.request_metadata.update_attributes!(:customer_accepts_responsibility=>true) if customer_accepts_responsibility
-
request.passed? ? request.change_decision! : request.fail!
-
end
-
end
-
1
private :fail_stock_well_requests
-
-
1
def pool_wells(wells)
-
_pool_wells(wells).
-
joins('LEFT OUTER JOIN uuids AS pool_uuids ON pool_uuids.resource_type="Submission" AND pool_uuids.resource_id=submission_id').
-
select('pool_uuids.external_id AS pool_uuid').
-
readonly(false).
-
tap do |wells_with_pool|
-
raise StandardError, "Cannot deal with a well in multiple pools" if wells_with_pool.group_by(&:id).any? { |_, multiple_pools| multiple_pools.uniq.size > 1 }
-
end
-
end
-
-
1
def _pool_wells(wells)
-
wells.pooled_as_target_by(TransferRequest)
-
end
-
1
private :_pool_wells
-
-
1
include Api::PlatePurposeIO::Extensions
-
-
1
self.per_page = 500
-
-
# TODO: change to purpose_id
-
1
has_many :plates, :foreign_key => :plate_purpose_id
-
-
1
def target_plate_type
-
self.target_type || 'Plate'
-
end
-
-
1
def self.stock_plate_purpose
-
# IDs copied from SNP
-
PlatePurpose.find(2)
-
end
-
-
1
def size
-
attributes['size']||96
-
end
-
-
1
def well_locations
-
in_preferred_order(Map.where_plate_size(size).where_plate_shape(asset_shape))
-
end
-
-
1
def in_preferred_order(relationship)
-
relationship.send("in_#{cherrypick_direction}_major_order")
-
end
-
-
1
def create!(*args, &block)
-
attributes = args.extract_options!
-
do_not_create_wells = !!args.first
-
-
attributes[:size] ||= size
-
attributes[:location] ||= default_location
-
attributes[:purpose] = self
-
-
target_plate_type.constantize.create_with_barcode!(attributes, &block).tap do |plate|
-
plate.wells.construct! unless do_not_create_wells
-
end
-
end
-
-
1
def cherrypick_in_rows?
-
cherrypick_direction == 'row'
-
end
-
-
1
def attatched?(plate)
-
true
-
end
-
-
1
def child_plate_purposes
-
child_purposes.where_is_a?(PlatePurpose)
-
end
-
-
1
def source_wells_for(stock_wells)
-
stock_wells
-
end
-
-
1
def supports_multiple_submissions?; false; end
-
-
end
-
1
module PlatePurpose::BroadcastLibraryComplete
-
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
super
-
prepare_library_complete(plate,user) if state == connect_on
-
end
-
-
1
private
-
-
1
def prepare_library_complete(plate,user)
-
orders = plate.orders_as_target.map(&:id)
-
generate_events_for(plate,orders,user)
-
end
-
-
1
def generate_events_for(plate,orders,user)
-
orders.each do |order_id|
-
BroadcastEvent::PlateLibraryComplete.create!(:seed=>plate,:user=>user,:properties=>{:order_id=>order_id})
-
end
-
end
-
-
1
def self.included(base)
-
2
base.class_eval do
-
2
class_attribute :connect_on
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
module PlatePurpose::Initial
-
1
def self.included(base)
-
3
base.class_eval do
-
3
include PlatePurpose::WorksOnLibraryRequests
-
end
-
end
-
-
# Initial plates in the pulldown pipelines change the state of the pulldown requests they are being
-
# created for to exactly the same state.
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
super
-
start_library_requests(plate,user)
-
end
-
-
# Ensure that the pulldown library creation request is started
-
1
def start_library_requests(plate,user)
-
orders = Set.new
-
each_well_and_its_library_request(plate) do |_, request|
-
if request.pending?
-
request.start!
-
orders << request.order_id
-
end
-
end
-
generate_events_for(plate,orders,user)
-
end
-
1
private :start_library_requests
-
-
1
def generate_events_for(plate,orders,user)
-
orders.each do |order_id|
-
BroadcastEvent::LibraryStart.create!(:seed=>plate,:user=>user,:properties=>{:order_id=>order_id})
-
end
-
end
-
1
private :generate_events_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
module PlatePurpose::Library
-
1
def self.included(base)
-
5
base.class_eval do
-
5
include PlatePurpose::WorksOnLibraryRequests
-
end
-
end
-
-
1
STATES_TO_ASSIGN_LIBRARY_INFORMATION = [ 'started', 'passed' ]
-
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
super
-
assign_library_information_to_wells(plate) if STATES_TO_ASSIGN_LIBRARY_INFORMATION.include?(state)
-
end
-
-
# Ensure that the library information within the aliquots of the well is correct.
-
1
def assign_library_information_to_wells(plate)
-
each_well_and_its_library_request(plate) do |well, library_request|
-
library_type, insert_size = library_request.library_type, library_request.insert_size
-
-
well.aliquots.each do |aliquot|
-
aliquot.library ||= well
-
aliquot.library_type ||= library_type
-
aliquot.insert_size ||= insert_size
-
aliquot.save!
-
end
-
end
-
end
-
1
private :assign_library_information_to_wells
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
module PlatePurpose::RequestAttachment
-
-
1
def transition_to(plate, state, user, contents = nil, customer_accepts_responsibility = false)
-
super
-
connect_requests(plate, state, contents)
-
end
-
-
1
def connect_requests(plate, state, contents = nil)
-
return unless state == connect_on
-
-
wells = plate.wells
-
wells = wells.located_at(contents) unless contents.blank?
-
-
wells.include_stock_wells.include_requests_as_target.each do |target_well|
-
-
source_wells = target_well.stock_wells
-
submission_ids = target_well.requests_as_target.map(&:submission_id)
-
-
source_wells.each do |source_well|
-
-
# We may have multiple requests out of each well, however we're only concerned
-
# about those associated with the active submission.
-
upstream = source_well.requests.detect do |r|
-
r.is_a?(connected_class) && submission_ids.include?(r.submission_id)
-
end
-
-
# We need to find the downstream requests BEFORE connecting the upstream
-
# This is because submission.next_requests tries to take a shortcut through
-
# the target_asset if it is defined.
-
if connect_downstream
-
downstream = upstream.submission.next_requests(upstream)
-
downstream.each { |ds| ds.update_attributes!(:asset => target_well) }
-
end
-
-
# In some cases, such as the Illumina-C pipelines, requests might be
-
# connected upfront. We don't want to touch these.
-
next unless upstream.target_asset.nil?
-
-
upstream.update_attributes!(:target_asset=> target_well)
-
upstream.pass!
-
-
end
-
end
-
end
-
-
1
def self.included(base)
-
4
base.class_eval do
-
4
class_attribute :connect_on
-
4
class_attribute :connect_downstream
-
4
class_attribute :connected_class
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
module PlatePurpose::Stock
-
1
def _pool_wells(wells)
-
wells.pooled_as_source_by(Request::LibraryCreation)
-
end
-
1
private :_pool_wells
-
-
1
UNREADY_STATE = 'pending'
-
1
READY_STATE = 'passed'
-
-
-
1
def state_of(plate)
-
# If there are no wells with aliquots we're pending
-
wells_with_aliquots = plate.wells.with_aliquots.all
-
return UNREADY_STATE if wells_with_aliquots.empty?
-
-
# All of the wells with aliquots must have requests for us to be considered passed
-
well_requests = Request::LibraryCreation.all(:conditions => { :asset_id => wells_with_aliquots.map(&:id) })
-
-
wells_states = well_requests.group_by(&:asset_id).map do |well_id, requests|
-
calculate_state_of_well(requests.map(&:state))
-
end
-
-
return UNREADY_STATE unless wells_states.count == wells_with_aliquots.count
-
return calculate_state_of_plate(wells_states)
-
end
-
-
1
def calculate_state_of_plate(wells_states)
-
unique_states = wells_states.uniq
-
return UNREADY_STATE if unique_states.include?(:unready)
-
case unique_states.sort
-
when ['failed'] then 'failed'
-
when ['cancelled'] then 'cancelled'
-
when ['cancelled','failed'] then 'failed'
-
else READY_STATE
-
end
-
end
-
-
1
def calculate_state_of_well(wells_states)
-
cancelled = wells_states.delete('cancelled') if wells_states.count > 1
-
return wells_states.first if wells_states.one?
-
return :unready if wells_states.size > 1
-
cancelled || :unready
-
end
-
-
1
def transition_state_requests(*args)
-
# Does nothing, we'll do it in a moment!
-
end
-
1
private :transition_state_requests
-
-
# The requests that we're going to be failing are based on the requests coming out of the
-
# wells, and the wells themselves, for stock plates.
-
1
def fail_request_details_for(wells)
-
wells.each do |well|
-
submission_ids = well.requests_as_source.map(&:submission_id)
-
yield(submission_ids, [well.id]) unless submission_ids.empty?
-
end
-
end
-
1
private :fail_request_details_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2015 Genome Research Ltd.
-
1
module PlatePurpose::WorksOnLibraryRequests
-
-
1
def each_well_and_its_library_request(plate, &block)
-
-
well_to_stock_id = Hash[plate.stock_wells.map { |well,stock_wells| [well.id, stock_wells.first.id] }]
-
requests = Request::LibraryCreation.for_asset_id(well_to_stock_id.values).include_request_metadata.group_by(&:asset_id)
-
-
plate.wells.all(:include => [{ :aliquots => :library },:requests_as_target]).each do |well|
-
next if well.aliquots.empty?
-
stock_id = well_to_stock_id[well.id] or raise "No stock well for #{well.id.inspect} (#{well_to_stock_id.inspect})"
-
stock_requests = requests[stock_id] or raise "No requests for stock well #{stock_id.inspect} (#{requests.inspect})"
-
stock_request = stock_requests.detect {|stock_request| stock_request.submission_id == well.requests_as_target.first.submission_id }
-
stock_request or raise "No requests for stock well #{stock_id.inspect} with matching submission (#{requests.inspect})"
-
yield(well, stock_request)
-
end
-
end
-
1
private :each_well_and_its_library_request
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2014 Genome Research Ltd.
-
1
class PlateTemplate < Plate
-
-
1
include Lot::Template
-
-
1
def update_params!(details = {})
-
self.name = details[:name]
-
self.wells.delete_all
-
self.size = (details[:rows]).to_i * (details[:cols]).to_i
-
set_control_well(details[:control_well]) unless set_control_well(details[:control_well]).nil?
-
self.save!
-
-
unless details[:wells].nil?
-
empty_wells = details[:wells].keys
-
empty_wells.each do |well|
-
self.add_well_by_map_description(Well.create!(), well)
-
end
-
end
-
end
-
-
1
def stamp_to(plate)
-
ActiveRecord::Base.transaction do
-
self.wells.each do |well|
-
plate.wells.located_at(well.map_description).first.aliquots = well.aliquots.map {|a| a.dup }
-
end
-
end
-
end
-
-
-
1
def set_control_well(result)
-
self.add_descriptor(Descriptor.new({:name => "control_well", :value => result}))
-
self.save
-
end
-
-
1
def control_well?
-
return false if descriptors.nil?
-
return 1 == descriptor_value('control_well').to_i
-
end
-
-
1
def without_control_wells?
-
return true if descriptors.nil?
-
return 0 == descriptor_value('control_well').to_i
-
end
-
-
1
scope :with_sizes, ->(sizes) {
-
where(["size IN (?)", sizes])
-
}
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class PlateTemplateTask < Task
-
1
include Tasks::PlatePurposeBehavior
-
-
1
class PlateTemplateData < Task::RenderElement
-
1
attr_reader :testing
-
1
alias_attribute :well, :asset
-
-
1
def initialize(request)
-
super(request)
-
# get all requests
-
# self.requests
-
# populate plates from Template
-
# display plates
-
end
-
-
-
end # class PlateTemplateData
-
-
1
def create_render_element(request)
-
request.asset && PlateTemplateData.new(request)
-
end
-
-
1
def partial
-
"plate_template_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_plate_template_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_plate_template_task(self, params)
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class PlateTransferTask < Task
-
-
1
belongs_to :purpose
-
-
1
def render_task(workflow, params)
-
workflow.render_plate_transfer_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_plate_transfer_task(self, params)
-
end
-
-
1
def partial
-
self.class.to_s.underscore.chomp('_task')
-
end
-
-
1
def included_for_render_task
-
[:pipeline]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
-
1
require 'carrierwave'
-
-
1
class PlateVolume < ActiveRecord::Base
-
1
extend DbFile::Uploader
-
-
1
has_uploaded :uploaded, { :serialization_column => "uploaded_file_name" }
-
-
1
before_save :calculate_barcode_from_filename
-
1
after_save :update_well_volumes
-
-
1
def calculate_barcode_from_filename
-
return if uploaded_file_name.blank?
-
match = uploaded_file_name.match(/^(\d+).csv/i)
-
return if match.nil?
-
self.barcode = match[1]
-
end
-
1
private :calculate_barcode_from_filename
-
-
1
def update_well_volumes
-
plate = Plate.include_wells_and_attributes.find_from_machine_barcode(barcode) or throw :no_source_plate
-
location_to_well = plate.wells.map_from_locations
-
-
extract_well_volumes do |well_description, volume|
-
map = Map.find_for_cell_location(well_description,plate.size) or raise "Cannot find location for #{well_description.inspect} on plate size #{plate.size}"
-
well = location_to_well[map]
-
well.well_attribute.update_attributes!(:measured_volume => volume.to_f) if well.present?
-
end
-
end
-
1
private :update_well_volumes
-
-
1
def extract_well_volumes
-
return if self.uploaded.nil?
-
head, *tail = CSV.parse(self.uploaded.file.read)
-
tail.each { |(barcode, location, volume)| yield(location, volume) }
-
end
-
1
private :extract_well_volumes
-
-
# Is an update required given the timestamp specified
-
1
def update_required?(modified_timestamp = Time.now)
-
self.updated_at < modified_timestamp
-
end
-
-
1
def call(filename, file)
-
return unless update_required?(file.stat.mtime)
-
db_files.map(&:destroy)
-
self.reload
-
update_attributes!(:uploaded_file_name => filename, :updated_at => file.stat.mtime, :uploaded => file)
-
end
-
-
1
class << self
-
1
def process_all_volume_check_files
-
all_plate_volume_file_names.each do |filename|
-
File.open(File.join(configatron.plate_volume_files, filename), 'r') do |file|
-
catch(:no_source_plate) { handle_volume(filename, file) }
-
end
-
end
-
end
-
-
1
def all_plate_volume_file_names
-
Dir.entries(configatron.plate_volume_files).reject { |f| File.directory?(File.join(configatron.plate_volume_files, f)) }
-
end
-
1
private :all_plate_volume_file_names
-
-
1
def handle_volume(filename, file)
-
ActiveRecord::Base.transaction do
-
find_for_filename(sanitized_filename(file)).call(filename, file)
-
end
-
rescue => exception
-
Rails.logger.warn("Error processing volume file #{filename}: #{exception.message}")
-
end
-
1
private :handle_volume
-
-
1
def sanitized_filename(file)
-
# We need to use the Carrierwave sanitized filename for lookup, else files with spaces are repetedly processed
-
# Later versions of carrierwave expose this sanitization better, but for now we are forced to create an object
-
CarrierWave::SanitizedFile.new(file).filename
-
end
-
-
1
def find_for_filename(filename)
-
self.find_by_uploaded_file_name(filename) or
-
->(filename, file) { PlateVolume.create!(:uploaded_file_name => filename, :updated_at => file.stat.mtime, :uploaded => file) }
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class PooledCherrypickRequest < Request
-
-
# Returns a list of attributes that must match for any given pool.
-
# We don't want to place any restrictions on Cherrypicking (Yet).
-
1
def shared_attributes
-
""
-
end
-
-
1
def transfer_aliquots
-
target_asset.aliquots << aliquots_for_transfer
-
end
-
-
1
private
-
-
1
def aliquots_for_transfer
-
asset.aliquots.map do |aliquot|
-
aliquot.dup.tap do |clone|
-
clone.study_id = initial_study_id || aliquot.study_id
-
clone.project_id = initial_project_id || aliquot.project_id
-
end
-
end.reject do |candidate_aliquot|
-
target_asset.aliquots.any? {|existing_aliquot| existing_aliquot.equivalent?(candidate_aliquot) }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
# Creating an instance of this class causes a child plate, with the specified plate type, to be created from
-
# the parent.
-
1
class PooledPlateCreation < AssetCreation
-
-
1
class ParentAssociation < ActiveRecord::Base
-
1
self.table_name =('asset_creation_parents')
-
1
belongs_to :asset_creation
-
1
belongs_to :parent, :class_name => 'Asset'
-
end
-
-
1
has_many :parent_associations, :foreign_key=>'asset_creation_id', :class_name => 'PooledPlateCreation::ParentAssociation'
-
-
# This is the child that is created from the parent. It cannot be assigned before validation.
-
1
has_many :parents, :through => :parent_associations, :class_name => 'Plate'
-
-
1
include_plate_named_scope :parents
-
-
1
def parent
-
parents.first
-
end
-
-
1
def record_creation_of_children
-
parents.each{|parent| parent.events.create_plate!(child_purpose, child, user)}
-
end
-
1
private :record_creation_of_children
-
-
1
def connect_parent_and_children
-
parents.each { |parent| AssetLink.create_edge!(parent, child) }
-
end
-
1
private :connect_parent_and_children
-
-
1
include PlateCreation::Children
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class PreCapturePool < ActiveRecord::Base
-
-
# We build pre capture groups at submission so that they are not affected by failing of wells or
-
# re-arraying.
-
-
1
module Poolable
-
1
def self.included(base)
-
1
base.class_eval do
-
1
has_one :pre_capture_pool, :through => :pooled_request
-
1
has_one :pooled_request, :dependent=>:destroy, :class_name => 'PreCapturePool::PooledRequest', :foreign_key => :request_id
-
end
-
end
-
end
-
-
1
class PooledRequest < ActiveRecord::Base
-
1
belongs_to :request
-
1
validates_presence_of :request_id
-
1
validates_uniqueness_of :request_id
-
1
belongs_to :pre_capture_pool
-
1
validates_presence_of :pre_capture_pool_id
-
end
-
-
1
include Uuid::Uuidable
-
1
has_many :requests, :through => :pooled_requests
-
1
has_many :pooled_requests, :dependent => :destroy
-
-
1
class Builder
-
-
1
attr_reader :submission
-
-
1
def initialize(submission)
-
@submission = submission
-
end
-
-
1
def poolable_type
-
@pt ||= RequestType.find(submission.request_type_ids).detect {|rt| rt.request_class.include?(Poolable)}
-
end
-
1
private :poolable_type
-
-
1
def library_creation_type
-
submission.request_type_ids.detect {|rt| RequestType.find(rt).request_class <= Request::LibraryCreation }
-
end
-
1
private :library_creation_type
-
-
1
def offset
-
@offset ||= (submission.request_type_ids.index(poolable_type.id) - submission.request_type_ids.index(library_creation_type))
-
end
-
1
private :offset
-
-
1
def pool(requests,plex)
-
requests.flatten.each_slice(plex) do |pooled_requests|
-
PreCapturePool.create!(:requests=>pooled_requests)
-
end
-
end
-
1
private :pool
-
-
1
def build!
-
ActiveRecord::Base.transaction do
-
return if poolable_type.nil?
-
submission.requests.find(:all, {
-
:joins => ['JOIN orders ON orders.id = requests.order_id','JOIN assets ON assets.id = requests.asset_id','JOIN maps ON maps.id = assets.map_id'],
-
:conditions=>['request_type_id = ?', library_creation_type ],
-
:order=>'maps.column_order ASC, id ASC'
-
}).group_by {|r| r.order.pre_cap_group||"o#{r.order_id}"}.each do |_,requests|
-
plex = requests.first.order.request_options['pre_capture_plex_level'].to_i
-
offset.times { requests.map!{|r| submission.next_requests(r) }}
-
pool(requests, plex)
-
end
-
end
-
end
-
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PrepKitBarcodeTask < Task
-
1
class PrepKitBarcodeData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && PrepKitBarcodeData.new(request)
-
end
-
-
1
def partial
-
"prep_kit_barcode_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_prep_kit_barcode_task(self, params)
-
end
-
-
1
def included_for_render_task
-
[:pipeline]
-
end
-
-
1
def included_for_do_task
-
[:pipeline,{:requests=>:target_asset}]
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_prep_kit_barcode_task(self, params)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
module Presenters
-
1
class BatchSubmenuPresenter
-
1
attr_reader :options
-
1
include Rails.application.routes.url_helpers
-
1
include ActionView::Helpers::TextHelper
-
-
1
private
-
1
def set_defaults(defaults)
-
@defaults=defaults
-
end
-
-
1
def initialize(current_user, batch)
-
@current_user = current_user
-
@batch = batch
-
@pipeline = @batch.pipeline
-
-
set_defaults({:controller => :batches, :id => @batch.id, :only_path => true})
-
end
-
-
1
def build_submenu
-
add_submenu_option "View summary", { :controller => :pipelines, :action => :summary }
-
add_submenu_option pluralize(@batch.comments.size, "comment" ), batch_comments_path(@batch)
-
load_pipeline_options
-
add_submenu_option "NPG run data", "#{configatron.run_data_by_batch_id_url}#{@batch.id}"
-
add_submenu_option "SybrGreen images", "#{configatron.sybr_green_images_url}#{@batch.id}"
-
end
-
-
1
def is_manager?
-
# The logic below is strange. It seems to block actions from owners who aren't also lab managers
-
# however still allows those without any role access.
-
!@current_user.is_owner? || @current_user.is_manager?
-
end
-
-
1
def is_pulldown_pipeline?
-
@pipeline.is_a?(PulldownMultiplexLibraryPreparationPipeline)
-
end
-
-
1
def is_multiplexed?
-
@batch.multiplexed?
-
end
-
-
1
def cherrypicking?
-
@pipeline.is_a?(CherrypickingPipeline)
-
end
-
-
1
def genotyping?
-
@pipeline.is_a?(GenotypingPipeline)
-
end
-
-
1
def pacbio?
-
@pipeline.is_a?(PacBioSequencingPipeline)
-
end
-
-
1
def not_sequencing?
-
!@pipeline.is_a?(SequencingPipeline)
-
end
-
-
1
def can_create_stock_assets?
-
@batch.pipeline.can_create_stock_assets?
-
end
-
-
1
def pacbio_sample_pipeline?
-
@pipeline.is_a?(PacBioSamplePrepPipeline)
-
end
-
-
1
def tube_layout_not_verified?
-
@batch.has_limit? and !@batch.has_event("Tube layout verified")
-
end
-
-
1
def has_plate_labels?
-
[ cherrypicking?, genotyping?, pacbio?, pacbio_sample_pipeline? ].any?
-
end
-
-
1
def has_stock_labels?
-
[ not_sequencing?, can_create_stock_assets?, !is_multiplexed?].all?
-
end
-
-
1
def load_pipeline_options
-
-
add_submenu_option "Edit batch", edit_batch_path(@batch) if is_manager?
-
-
# Printing of labels is enabled for anybody
-
add_submenu_option "Print labels", :print_labels if is_pulldown_pipeline?
-
add_submenu_option "Print pool label", :print_multiplex_labels if is_multiplexed?
-
add_submenu_option "Print labels" , :print_labels if is_multiplexed?
-
add_submenu_option "Print stock pool label" , :print_stock_multiplex_labels if is_multiplexed?
-
add_submenu_option "Print plate labels" , :print_plate_labels if has_plate_labels?
-
add_submenu_option "Print stock labels" , :print_stock_labels if has_stock_labels?
-
add_submenu_option "Print labels" , :print_labels if not_sequencing?
-
-
# Other options are enabled only for managers
-
if is_manager?
-
add_submenu_option "Vol' & Conc'", :edit_volume_and_concentration if not_sequencing?
-
add_submenu_option "Create stock tubes" , :new_stock_assets if can_create_stock_assets?
-
add_submenu_option "Print sample prep worksheet" , :sample_prep_worksheet if pacbio_sample_pipeline?
-
-
if @pipeline.prints_a_worksheet_per_task? and !pacbio_sample_pipeline?
-
@tasks.each do |task|
-
add_submenu_option "Print worksheet for #{task.name}" , {:action => :print, :task_id => task.id}
-
end
-
else
-
add_submenu_option "Print worksheet" , :print
-
end
-
-
add_submenu_option "Verify tube layout" , :verify if tube_layout_not_verified?
-
add_submenu_option "Batch Report", :pulldown_batch_report if is_pulldown_pipeline?
-
end
-
end
-
-
1
public
-
-
1
def add_submenu_option(text, action_params)
-
@options ||= Array.new
-
-
# If it is a string, it will be an url
-
unless action_params.is_a?(String)
-
# If it is a symbol, it will be the action
-
# If not, it will be a Hash with the new content (controller, action, ...)
-
if (action_params.is_a?(Symbol))
-
action_params = { :action => action_params }
-
end
-
actionConfig = @defaults.dup
-
action_params.each_pair do |key, value|
-
actionConfig[key] = value
-
end
-
action_params = url_for(actionConfig)
-
end
-
@options += [{:label => text, :url => action_params}]
-
end
-
-
1
def each_option
-
build_submenu if @options.nil?
-
@options.each do |option|
-
yield option
-
end
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module Presenters
-
1
class GroupedPipelineInboxPresenter
-
-
1
class << self
-
-
1
def fields
-
11
@fields ||= []
-
end
-
-
1
def add_field(name,method,options={})
-
11
fields << [name,method,options[:if]]
-
end
-
-
end
-
-
# Register our fields and their respective conditions
-
# TODO: Drive some of these directly from the database
-
1
add_field 'Internal ID', :internal_id
-
1
add_field 'Barcode', :barcode
-
1
add_field 'Wells', :wells, :if => :purpose_important?
-
1
add_field 'Plate Purpose', :plate_purpose, :if => :purpose_important?
-
1
add_field 'Pick To', :pick_to, :if => :purpose_important?
-
1
add_field 'Next Pipeline', :next_pipeline, :if => :display_next_pipeline?
-
1
add_field 'Submission', :submission_id, :if => :group_by_submission?
-
1
add_field 'Study', :study, :if => :group_by_submission?
-
1
add_field 'Stock Barcode', :stock_barcode, :if => :show_stock?
-
1
add_field 'Still Required', :still_required, :if => :select_partial_requests?
-
1
add_field 'Submitted at', :submitted_at
-
-
-
1
attr_reader :pipeline, :user
-
-
1
def initialize(pipeline,user,show_held_requests=false)
-
@pipeline = pipeline
-
@user = user
-
@show_held_requests = show_held_requests
-
end
-
-
1
def each_field_header
-
valid_fields.each do |field, method, condition|
-
yield field
-
end
-
end
-
-
1
def each_method
-
valid_fields.each do |field, method, condition|
-
yield method
-
end
-
end
-
-
# Yields a line presenter
-
1
def each_line
-
grouped_requests.each_with_index do |request,index|
-
group = [request.container_id, request.submission_id]
-
yield GroupLinePresenter.new(group, request,index,pipeline,self)
-
end
-
end
-
-
1
def field_count
-
valid_fields.size
-
end
-
-
1
private
-
-
1
def grouped_requests
-
@request_groups ||= @pipeline.grouped_requests(@show_held_requests)
-
end
-
-
1
def valid_fields
-
@valid_fields ||= self.class.fields.select {|n,m,c| c.nil? || self.send(c) }
-
end
-
-
1
def purpose_important?
-
pipeline.purpose_information?
-
end
-
-
1
def display_next_pipeline?
-
pipeline.display_next_pipeline?
-
end
-
-
1
def select_partial_requests?
-
!pipeline.purpose_information?
-
end
-
-
1
def show_stock?
-
!pipeline.purpose_information?
-
end
-
-
1
def group_by_submission?
-
pipeline.group_by_submission?
-
end
-
-
end
-
-
1
class GroupLinePresenter
-
-
1
include PipelinesHelper
-
-
1
attr_reader :group, :request, :index, :pipeline, :inbox
-
1
def initialize(group,request,index,pipeline,inbox)
-
@group, @request, @index,@pipeline,@inbox = group,request,index,pipeline,inbox
-
end
-
-
1
def group_id
-
group.join(", ")
-
end
-
-
1
def request_group_id
-
"request_group_#{ group_id.gsub(/[^a-z0-9]+/, '_') }"
-
end
-
-
1
def parent
-
@parent ||= Asset.find(group.first)
-
end
-
-
1
def submission_id
-
request.submission_id
-
end
-
-
1
def submission
-
request.submission
-
end
-
-
1
def submitted_at
-
request.submitted_at
-
end
-
-
1
def submission_name
-
submission.name if submission_id.present?
-
end
-
-
1
def priority
-
request.max_priority
-
end
-
-
1
def each_field
-
inbox.each_method do |method|
-
yield send(method)
-
end
-
end
-
-
1
def internal_id
-
parent.id
-
end
-
-
1
def barcode
-
parent.sanger_human_barcode
-
end
-
-
1
def wells
-
request.request_count
-
end
-
-
1
def plate_purpose
-
parent.purpose.name
-
end
-
-
1
def pick_to
-
target_purpose_for(request)
-
end
-
-
1
def next_pipeline
-
next_pipeline_name_for(request)
-
end
-
-
1
def study
-
submission.study_names if submission_id.present?
-
end
-
-
1
def stock_barcode
-
parent.source_plate.try(:sanger_human_barcode)||"Unknown"
-
end
-
-
1
def still_required
-
wells/parent.height
-
end
-
-
# Gates
-
-
1
def groupless?
-
yield if group.blank?
-
end
-
-
1
def standard_fields?
-
yield unless parent.nil?
-
end
-
-
1
def parentless?
-
yield if parent.nil?
-
end
-
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
class Presenters::QcReportPresenter
-
-
REPORT_IDENTITY = 'Sequencescape QC Report'
-
VERSION = '1.0.0'
-
HEADER_FIELDS = {
-
'Study' => :study_name,
-
'Product' => :product_name,
-
'Criteria Version' => :criteria_version,
-
'Report Identifier'=> :report_identifier,
-
'Generated on' => :created_date,
-
'Contents' => :new_or_all
-
}
-
-
attr_reader :qc_report, :queue_count
-
-
def initialize(qc_report,queue_count=0)
-
@qc_report = qc_report
-
@queue_count = queue_count
-
end
-
-
def filename
-
"#{report_identifier}.csv"
-
end
-
-
def criteria_version
-
"#{qc_report.product_criteria.stage}_#{qc_report.product_criteria.version}"
-
end
-
-
def product_name
-
qc_report.product.name
-
end
-
-
def study_name
-
qc_report.study.name
-
end
-
-
def study_abbreviation
-
qc_report.study.abbreviation
-
end
-
-
-
def state
-
qc_report.state.humanize
-
end
-
-
def new_or_all
-
qc_report.exclude_existing ? 'New samples' : 'All samples'
-
end
-
-
def created_date
-
qc_report.created_at.to_formatted_s(:rfc822)
-
end
-
-
def state_description
-
I18n.t(qc_report.state, :scope=>'qc_reports.state_descriptions', :default => :default, :queue_count => queue_count )
-
end
-
-
def to_csv(io)
-
@csv = CSV.new(io)
-
csv_headers
-
@csv << [] # Pad with an empty line
-
csv_field_headers
-
csv_body
-
@csv
-
end
-
1
-
delegate :available?, :study, :report_identifier, :to => :qc_report
-
-
1
def each_header
-
HEADER_FIELDS.each do |field,lookup|
-
yield [field,send(lookup)]
-
end
-
end
-
-
1
private
-
-
# Information about the qc_report itself
-
1
def csv_headers
-
@csv << [REPORT_IDENTITY,VERSION]
-
@csv << [I18n.t('qc_reports.fixed_content')]
-
@csv << [I18n.t('qc_reports.instruction')]
-
each_header do |pair|
-
@csv << pair
-
end
-
end
-
-
1
def criteria_headers
-
@criteria_headers ||= qc_report.product_criteria.headers
-
end
-
-
# The headers for the qc information table
-
1
def csv_field_headers
-
@csv << ['Asset ID'] + criteria_headers.map {|h| h.to_s.humanize } + ['Qc Decision','Proceed']
-
end
-
-
1
def csv_body
-
qc_report.qc_metrics.each do |m|
-
@csv << [m.asset_id] + criteria_headers.map {|h| m.metrics[h] } + [m.qc_decision,m.human_proceed]
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
PrintBarcode= Sanger::Barcode::Printing
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
class Product < ActiveRecord::Base
-
-
1
include SharedBehaviour::Indestructable
-
1
include SharedBehaviour::Deprecatable
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :scope => :deprecated_at
-
1
has_many :product_product_catalogues, :dependent => :destroy
-
1
has_many :product_catalogues, :through => :product_product_catalogues
-
1
has_many :submission_templates, :inverse_of => :product, :through => :product_catalogues
-
1
has_many :orders
-
1
has_many :product_criteria, :inverse_of => :product, :class_name => 'ProductCriteria'
-
-
1
scope :with_stock_report, ->() {
-
joins(:product_criteria).
-
where(:product_criteria => {:deprecated_at=>nil,:stage=>ProductCriteria::STAGE_STOCK})
-
}
-
-
1
scope :alphabetical, ->() { order('name ASC') }
-
-
1
def stock_criteria
-
product_criteria.active.stock.first
-
end
-
-
1
def display_name
-
deprecated? ? "#{name} (Deprecated #{deprecated_at.to_formatted_s(:iso8601)})": name
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# Product catalogues provide a means of associating products with a submission
-
# template. selection_behaviour can allow a submission template to
-
# select an appropriate product.
-
# Ideally we want to deprecate submission templates in favour of
-
# products.
-
-
1
class ProductCatalogue < ActiveRecord::Base
-
-
1
UndefinedBehaviour = Class.new(StandardError)
-
-
1
has_many :submission_templates, :inverse_of => :product_catalogue
-
1
has_many :product_product_catalogues, :inverse_of => :product_catalogue, :dependent => :destroy
-
1
has_many :products, :through => :product_product_catalogues
-
-
1
validates_presence_of :name
-
1
validates_presence_of :selection_behaviour
-
1
validate :selection_behaviour_exists?, :if => :selection_behaviour?
-
-
1
class << self
-
1
def construct!(arguments)
-
ActiveRecord::Base.transaction do
-
products = arguments.delete(:products)
-
product_assocations = products.map do |criterion,product_name|
-
{
-
:selection_criterion => criterion,
-
:product => Product.find_or_create_by_name(product_name)
-
}
-
end
-
self.create!(arguments) do |catalogue|
-
catalogue.product_product_catalogues.build(product_assocations)
-
end
-
end
-
end
-
-
end
-
-
1
def product_for(submission_attributes)
-
selection_class.new(self,submission_attributes).product
-
end
-
-
1
def product_with_criteria(criteria)
-
products.find(:first,:conditions=>{:product_product_catalogues=>{:selection_criterion=>criteria}})
-
end
-
-
1
private
-
-
1
def selection_behaviour_exists?
-
# We can't use const_defined? here as it doesn't trigger rails autoloading.
-
# We could probably use the autoloading API more directly, but it doesn't
-
# seem to be intended to be used outside of Rails itself.
-
ProductCatalogue.const_get(selection_behaviour)
-
true
-
rescue NameError
-
errors.add(:selection_behaviour,"#{selection_behaviour} is not recognized")
-
false
-
end
-
-
1
def selection_class
-
raise UndefinedBehaviour, "No selection behaviour names #{selection_behaviour}" unless selection_behaviour_exists?
-
ProductCatalogue.const_get(selection_behaviour)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ProductCatalogue::LibraryDriven
-
-
1
attr_reader :product
-
-
1
def initialize(catalogue,submission_attributes)
-
@product = catalogue.products.first
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ProductCatalogue::Manual
-
-
1
attr_reader :product
-
-
1
def initialize(catalogue,submission_attributes)
-
@product = catalogue.product_with_criteria(submission_attributes[:order_role])||catalogue.product_with_criteria(nil)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class ProductCatalogue::SingleProduct
-
-
1
attr_reader :product
-
-
1
def initialize(catalogue,submission_attributes)
-
@product = catalogue.products.first
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
class ProductCriteria < ActiveRecord::Base
-
-
1
STAGE_STOCK = 'stock'
-
-
-
# By default rails will try and name the table 'product_criterias'
-
# We don't use the singular 'ProductCriterion' as the class name
-
# as a single record may define multiple criteria.
-
1
self.table_name=('product_criteria')
-
-
1
belongs_to :product
-
1
validates_presence_of :product, :stage, :behaviour
-
-
1
validates_uniqueness_of :stage, :scope => [:product_id,:deprecated_at]
-
1
validate :behaviour_exists?, :if => :behaviour?
-
-
1
serialize :configuration
-
-
1
scope :for_stage, ->(stage) { where(:stage=>stage) }
-
1
scope :stock, ->() { where(:stage=>STAGE_STOCK) }
-
1
scope :older_than, ->(id) { wheere(['id < ?',id]) }
-
-
1
before_create :set_version_number
-
-
1
include SharedBehaviour::Indestructable
-
1
include SharedBehaviour::Deprecatable
-
1
include SharedBehaviour::Immutable
-
-
-
1
def assess(asset)
-
ProductCriteria.const_get(behaviour).new(configuration,asset)
-
end
-
-
1
def headers
-
ProductCriteria.const_get(behaviour).headers(configuration)
-
end
-
-
1
private
-
-
1
def behaviour_exists?
-
# We can't use const_defined? here as it doesn't trigger rails autoloading.
-
# We could probably use the autoloading API more directly, but it doesn't
-
# seem to be intended to be used outside of Rails itself.
-
ProductCriteria.const_get(behaviour)
-
true
-
rescue NameError
-
errors.add(:behaviour,"#{behaviour} is not recognized")
-
false
-
end
-
-
1
def set_version_number
-
v = product.product_criteria.for_stage(stage).count + 1
-
self.version = v
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# Advanced Product Criteria can have 'unprocessable' thresholds
-
# as well as fails.
-
1
class ProductCriteria::Advanced < ProductCriteria::Basic
-
-
1
attr_reader :qc_decision
-
-
1
STATE_ORDER = ['failed','unprocessable']
-
-
1
def invalid(attribute,message,decision)
-
@qc_decision = decision
-
@comment << message % attribute.to_s.humanize
-
@comment.uniq!
-
end
-
-
1
def assess!
-
@qc_decision = 'passed'
-
STATE_ORDER.each do |decision|
-
params.fetch(decision,[]).each do |attribute,comparisons|
-
value = fetch_attribute(attribute)
-
values[attribute] = value
-
-
if value.blank? && comparisons.present?
-
invalid(attribute,'%s has not been recorded',decision)
-
next
-
end
-
-
comparisons.each do |comparison,target|
-
value.send(method_for(comparison),target) || invalid(attribute,message_for(comparison),decision)
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
class ProductCriteria::Basic
-
-
SUPPORTED_WELL_ATTRIBUTES = [:gel_pass, :concentration, :current_volume, :pico_pass, :gender_markers, :gender, :measured_volume, :initial_volume, :molarity, :sequenom_count]
-
SUPPORTED_SAMPLE = [:sanger_sample_id]
-
SUPPORTED_SAMPLE_METADATA = [:gender, :sample_ebi_accession_number, :supplier_name]
-
EXTENDED_ATTRIBUTES = [:total_micrograms, :conflicting_gender_markers, :sample_gender, :well_location, :plate_barcode]
-
-
PASSSED_STATE = 'passed'
-
FAILED_STATE = 'failed'
-
-
attr_reader :passed, :params, :comment, :values
-
alias_method :passed?, :passed
-
-
Comparison = Struct.new(:method,:message)
-
-
METHOD_ALIAS = {
-
:greater_than => Comparison.new(:>, '%s too low' ),
-
:less_than => Comparison.new(:<, '%s too high'),
-
:at_least => Comparison.new(:>=, '%s too low' ),
-
:at_most => Comparison.new(:<=, '%s too high'),
-
:equals => Comparison.new(:==, '%s not suitable')
-
}
-
-
GENDER_MARKER_MAPS = {
-
'male' => 'M',
-
'female' => 'F'
-
}
-
-
class << self
-
# Returns a list of possible criteria to either display or validate
-
def available_criteria
-
SUPPORTED_WELL_ATTRIBUTES + EXTENDED_ATTRIBUTES + SUPPORTED_SAMPLE_METADATA + SUPPORTED_SAMPLE
-
end
-
-
def headers(configuration)
-
configuration.map {|k,v| k } + [:comment]
-
end
-
end
-
-
def initialize(params,well)
-
@params = params
-
@well_or_metric = well
-
@comment = []
-
@values = {}
-
assess!
-
end
-
-
def total_micrograms
-
return nil if measured_volume.nil? || concentration.nil?
-
(measured_volume * concentration) / 1000.0
-
end
-
-
def conflicting_gender_markers
-
(gender_markers||[]).select {|marker| conflicting_marker?(marker)}.count
-
end
-
-
def metrics
-
values.merge({:comment => @comment.join(';')})
-
end
-
-
def well_location
-
@well_or_metric.map_description
-
end
-
-
def plate_barcode
-
@well_or_metric.plate.try(:sanger_human_barcode) || "Unknown"
-
end
-
-
SUPPORTED_SAMPLE.each do |attribute|
-
delegate(attribute, :to => :sample, :allow_nil => true)
-
end
-
-
delegate(:sample_metadata, :to => :sample, :allow_nil => true)
-
-
SUPPORTED_SAMPLE_METADATA.each do |attribute|
-
delegate(attribute, :to => :sample_metadata, :allow_nil => true)
-
end
-
1
-
SUPPORTED_WELL_ATTRIBUTES.each do |attribute|
-
delegate(attribute, :to => :well_attribute, :allow_nil => true)
-
end
-
-
# Return the sample gender, returns nil if it can't be determined
-
# ie. mixed input, or not male/female
-
1
def sample_gender
-
markers = @well_or_metric.samples.map {|s| s.sample_metadata.gender && s.sample_metadata.gender.downcase.strip }.uniq
-
return nil if markers.count > 1
-
GENDER_MARKER_MAPS[markers.first]
-
end
-
-
1
def qc_decision
-
passed? ? PASSSED_STATE : FAILED_STATE
-
end
-
-
1
private
-
-
1
def well_attribute
-
@well_or_metric.well_attribute
-
end
-
-
1
def sample
-
@well_or_metric.samples.first
-
end
-
-
1
def conflicting_marker?(marker)
-
expected = sample_gender
-
return false if expected.nil?
-
return false unless known_marker?(marker)
-
marker != expected
-
end
-
-
1
def known_marker?(marker)
-
GENDER_MARKER_MAPS.values.include?(marker)
-
end
-
-
1
def invalid(attribute,message)
-
@passed = false
-
@comment << message % attribute.to_s.humanize
-
@comment.uniq!
-
end
-
-
1
def assess!
-
@passed = true
-
params.each do |attribute,comparisons|
-
value = fetch_attribute(attribute)
-
values[attribute] = value
-
-
if value.blank? && comparisons.present?
-
invalid(attribute,'%s has not been recorded')
-
next
-
end
-
-
comparisons.each do |comparison,target|
-
value.send(method_for(comparison),target) || invalid(attribute,message_for(comparison))
-
end
-
end
-
end
-
-
# If @well_or_metric is a hash, then we are re-assessing the original criteria
-
#
-
# Note: This gives us the result at the time the criteria were
-
# originally run, and doesn't take into account subsequent changes
-
# in the well. This is useful if the metric has gone through multiple manual states.
-
# This probably won't get callled for the basic class, but may be used for subclasses
-
1
def fetch_attribute(attribute)
-
if @well_or_metric.is_a?(Hash)
-
@well_or_metric[attribute]
-
else
-
self.send(attribute)
-
end
-
end
-
-
1
def method_for(comparison)
-
METHOD_ALIAS[comparison].method || raise(UnknownSpecification, "#{comparison} isn't a recognised means of comparison.")
-
end
-
-
1
def message_for(comparison)
-
METHOD_ALIAS[comparison].message || raise(UnknownSpecification, "#{comparison} isn't a recognised means of comparison.")
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class ProductLine < ActiveRecord::Base
-
-
1
has_many :request_types
-
1
has_many :submission_templates
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# Association between a product and a catalogue.
-
# selection_criteria provides a means for catalogues with multiple
-
# products to select a suitable one.
-
-
1
class ProductProductCatalogue < ActiveRecord::Base
-
-
1
belongs_to :product
-
1
belongs_to :product_catalogue
-
-
1
validates_presence_of :product
-
1
validates_presence_of :product_catalogue
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014,2015 Genome Research Ltd.
-
-
require 'aasm'
-
-
class Project < ActiveRecord::Base
-
include Api::ProjectIO::Extensions
-
include ModelExtensions::Project
-
include Api::Messages::FlowcellIO::ProjectExtensions
-
-
-
self.per_page = 500
-
include EventfulRecord
-
include AASM
-
include Uuid::Uuidable
-
include SharedBehaviour::Named
-
extend EventfulRecord
-
-
def self.states
-
Project.aasm_states.map(&:name)
-
end
-
-
ACTIVE_STATE = 'active'
-
-
has_many_events
-
has_many_lab_events
-
-
aasm_column :state
-
aasm_initial_state :pending
-
aasm_state :pending
-
aasm_state :active
-
aasm_state :inactive
-
-
aasm_event :reset do
-
transitions :to => :pending, :from => [:inactive, :active]
-
end
-
-
aasm_event :activate do
-
transitions :to => :active, :from => [:pending, :inactive]
-
end
-
-
aasm_event :deactivate do
-
transitions :to => :inactive, :from => [:pending, :active]
-
end
-
-
scope :in_assets, ->(assets) {
-
select('DISTINCT projects.*').
-
joins([
-
'LEFT JOIN aliquots ON aliquots.project_id = projects.id',
-
]).
-
where(['aliquots.receptacle_id IN (?)',assets.map(&:id)])
-
}
-
-
has_many :roles, :as => :authorizable
-
has_many :orders
-
has_many :studies, :class_name => "Study", :through => :orders, :source => :study, :uniq => true
-
has_many :submissions, :through => :orders, :source => :submission, :uniq => true
-
has_many :sample_manifests
-
-
validates_presence_of :name, :state
-
validates_uniqueness_of :name, :on => :create, :message => "already in use (#{self.name})"
-
-
scope :for_search_query, ->(query,with_includes) {
-
where([ 'name LIKE ? OR id=?', "%#{query}%", query ])
-
}
-
-
# Allow us to pass in nil or '' if we don't want to filter state.
-
# State is required so we don't need to look up an actual null state
-
scope :in_state, ->(state) {
-
state.present? ? where(state:state) : where(true)
-
}
-
-
scope :active, ->() { where(state: ACTIVE_STATE) }
-
scope :approved, ->() { where(approved: true) }
-
scope :unapproved, ->() { where(approved: false) }
-
scope :valid, ->() { active.approved }
-
scope :alphabetical, ->() { order('name ASC') }
-
scope :for_user, ->(user) { joins({:roles=>:user_role_bindings}).where(:roles_users=>{:user_id=>user}) }
-
-
scope :with_unallocated_manager, ->() {
-
roles = Role.arel_table
-
joins(:roles).on(roles[:name].eq('manager')).where(roles[:id].eq(nil))
-
}
-
-
def ended_billable_lanes(ended)
-
events = []
-
self.samples.each do |sample|
-
if sample.ended.downcase == ended.downcase
-
events << sample.billable_events
-
end
-
end
-
events = events.flatten
-
end
-
-
def billable_events
-
e = []
-
self.samples.each do |sample|
-
e << sample.billable_events
-
end
-
e.flatten
-
end
-
-
def billable_events_between(from, to)
-
a = []
-
billable_events.each do |event|
-
if event.created_at.to_date >= from and event.created_at.to_date <= to
-
a << event
-
end
-
end
-
a
-
end
-
-
def ended_billable_lanes_between(from, to, ended)
-
events = ended_billable_lanes(ended)
-
-
a = []
-
events.each do |event|
-
if event.created_at.to_date >= from and event.created_at.to_date <= to
-
a << event
-
end
-
end
-
a.size
-
end
-
-
def billable_lanes_between(from, to)
-
billable_events_between(from, to).size
-
end
-
-
def owners
-
role = self.roles.detect{|r| r.name == "owner" }
-
unless role.nil?
-
role.users
-
else
-
[]
-
end
-
end
-
-
def owner
-
owners_ = owners
-
owners_ and owners_.first
-
end
-
-
def manager
-
role = self.roles.detect{|r| r.name == "manager"}
-
unless role.nil?
-
role.users.first
-
else
-
nil
-
end
-
end
-
-
def actionable?
-
self.project_metadata.budget_division.name != 'Unallocated'
-
end
-
-
def submittable?
-
return true if project_metadata.project_funding_model.present?
-
errors.add(:base,"No funding model specified")
-
false
-
end
-
-
def r_and_d?
-
self.project_metadata.budget_division.name == configatron.r_and_d_division
-
end
-
-
def sequencing_budget_division
-
self.project_metadata.budget_division.name
-
end
-
-
alias_attribute :friendly_name, :name
-
1
-
delegate :project_cost_code, :to=> :project_metadata
-
-
1
PROJECT_FUNDING_MODELS = [
-
'',
-
"Internal",
-
"External",
-
"External - own machine"
-
]
-
-
1
extend Metadata
-
1
has_metadata do
-
# NOTE: The following attribute is not required for Microarray Genotyping.
-
# I think this might be broken and suggests that there should be separate classes for project: one for
-
# next-gen sequencing that includes this attribute in it's metadata, and one for microarray genotyping
-
# that doesn't.
-
1
include ProjectManager::Associations
-
1
include BudgetDivision::Associations
-
-
1
attribute(:project_cost_code, :required => true)
-
1
attribute(:funding_comments)
-
1
attribute(:collaborators)
-
1
attribute(:external_funding_source)
-
1
attribute(:sequencing_budget_cost_centre)
-
1
attribute(:project_funding_model, :in => PROJECT_FUNDING_MODELS)
-
1
attribute(:gt_committee_tracking_id)
-
-
1
before_validation do |record|
-
record.project_cost_code = nil if record.project_cost_code.blank?
-
record.project_funding_model = nil if record.project_funding_model.blank?
-
end
-
end
-
-
1
def subject_type
-
'project'
-
end
-
-
1
scope :with_unallocated_budget_division, -> { joins(:project_metadata).where(:project_metadata => { :budget_division_id => BudgetDivision.find_by_name('Unallocated') }) }
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class ProjectManager < ActiveRecord::Base
-
1
extend Attributable::Association::Target
-
-
1
has_many :project
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :message => "of project manager already present in database"
-
-
1
module Associations
-
1
def self.included(base)
-
1
base.validates_presence_of :project_manager_id
-
1
base.belongs_to :project_manager
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
class Pulldown::InitialDownstreamPlatePurpose < IlluminaHtp::InitialDownstreamPlatePurpose
-
# Initial plates in the pulldown pipelines change the state of the pulldown requests they are being
-
# created for to exactly the same state.
-
-
1
def stock_wells(plate,contents)
-
return plate.parents.map {|parent| parent.wells}.flatten unless contents.present?
-
Well.find(:all, :joins => :requests, :conditions => {:requests => {:target_asset_id => plate.wells.located_at(contents).map(&:id)}})
-
end
-
-
1
def supports_multiple_submissions?; true; end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
# Specialised implementation of the plate purpose for the initial plate types in the Pulldown pipelines:
-
# WGS Covaris, SC Covaris, ISC Covaris.
-
1
class Pulldown::InitialPlatePurpose < PlatePurpose
-
1
def transition_to(plate, state, user, contents = nil,customer_accepts_responsibility=false)
-
ActiveRecord::Base.transaction do
-
super
-
new_outer_state = ['started','passed','qc_complete','nx_in_progress'].include?(state) ? 'started' : state
-
outer_requests(plate,contents).each do |request|
-
# request.customer_accepts_responsibility! if customer_accepts_responsibility
-
request.transition_to(new_outer_state) if request.pending?
-
end
-
end
-
end
-
-
1
def outer_requests(plate,contents)
-
well_ids = contents.present? ? plate.wells.located_at(contents).map(&:id) : plate.wells.map(&:id)
-
transfer_request_sti = [TransferRequest, *TransferRequest.descendants].map(&:name).map(&:inspect).join(',')
-
Request.find(:all, {
-
:select => "requests.*",
-
:joins => [
-
"INNER JOIN requests AS asctf ON asctf.asset_id = requests.asset_id AND asctf.sti_type IN (#{transfer_request_sti})"
-
],
-
:conditions => ["asctf.target_asset_id IN (?) AND NOT requests.sti_type IN (#{transfer_request_sti})", plate.wells.map(&:id)]
-
})
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class Pulldown::LibraryPlatePurpose < PlatePurpose
-
1
include PlatePurpose::Library
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
-
# We require all the plate and tube purpose files here as Rails eager loading does not play nicely with single table
-
# inheritance
-
-
1
module Pulldown::PlatePurposes
-
-
1
ISCH_PURPOSE_FLOWS = [[
-
'Lib PCR-XP',
-
'ISCH lib pool',
-
'ISCH hyb',
-
'ISCH cap lib',
-
'ISCH cap lib PCR',
-
'ISCH cap lib PCR-XP',
-
'ISCH cap lib pool'
-
]]
-
-
1
PLATE_PURPOSE_FLOWS = [
-
[
-
'WGS stock DNA',
-
'WGS Covaris',
-
'WGS post-Cov',
-
'WGS post-Cov-XP',
-
'WGS lib',
-
'WGS lib PCR',
-
'WGS lib PCR-XP',
-
'WGS lib pool'
-
], [
-
'SC stock DNA',
-
'SC Covaris',
-
'SC post-Cov',
-
'SC post-Cov-XP',
-
'SC lib',
-
'SC lib PCR',
-
'SC lib PCR-XP',
-
'SC hyb',
-
'SC cap lib',
-
'SC cap lib PCR',
-
'SC cap lib PCR-XP',
-
'SC cap lib pool'
-
], [
-
'ISC stock DNA',
-
'ISC Covaris',
-
'ISC post-Cov',
-
'ISC post-Cov-XP',
-
'ISC lib',
-
'ISC lib PCR',
-
'ISC lib PCR-XP',
-
'ISC lib pool',
-
'ISC hyb',
-
'ISC cap lib',
-
'ISC cap lib PCR',
-
'ISC cap lib PCR-XP',
-
'ISC cap lib pool'
-
], ISCH_PURPOSE_FLOWS.first
-
]
-
-
1
PLATE_PURPOSE_TYPE = {
-
'ISCH lib pool' => Pulldown::InitialDownstreamPlatePurpose,
-
'ISCH hyb' => IlluminaHtp::DownstreamPlatePurpose,
-
'ISCH cap lib' => IlluminaHtp::DownstreamPlatePurpose,
-
'ISCH cap lib PCR' => IlluminaHtp::DownstreamPlatePurpose,
-
'ISCH cap lib PCR-XP' => IlluminaHtp::DownstreamPlatePurpose,
-
'ISCH cap lib pool' => IlluminaHtp::DownstreamPlatePurpose
-
}
-
-
1
PLATE_PURPOSE_LEADING_TO_QC_PLATES = [
-
'WGS post-Cov',
-
'WGS post-Cov-XP',
-
'WGS lib PCR-XP',
-
-
'SC post-Cov',
-
'SC post-Cov-XP',
-
'SC lib PCR-XP',
-
'SC cap lib PCR-XP',
-
-
'ISC post-Cov',
-
'ISC post-Cov-XP',
-
'ISC lib PCR-XP',
-
'ISC cap lib PCR-XP'
-
]
-
-
-
1
PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES = [
-
[ 'Lib PCR-XP', 'ISCH lib pool', 'Pulldown::Requests::PcrXpToIscLibPool' ],
-
[ 'Lib PCRR-XP', 'ISCH lib pool', 'Pulldown::Requests::PcrXpToIscLibPool' ]
-
]
-
-
1
STOCK_PLATE_PURPOSES = ['WGS stock DNA','SC stock DNA','ISC stock DNA']
-
-
1
class << self
-
-
1
def create_purposes(branch_o)
-
branch = branch_o.clone
-
initial = Purpose.find_by_name!(branch.shift)
-
branch.inject(initial) do |parent,new_purpose_name|
-
Pulldown::PlatePurposes::PLATE_PURPOSE_TYPE[new_purpose_name].create!(:name => new_purpose_name).tap do |child_purpose|
-
parent.child_relationships.create!(:child => child_purpose, :transfer_request_type => request_type_between(parent,child_purpose))
-
end
-
end
-
end
-
-
1
def request_type_between(parent, child)
-
_, _, request_class = self::PLATE_PURPOSES_TO_REQUEST_CLASS_NAMES.detect { |a,b,_| (parent.name == a) && (child.name == b) }
-
return RequestType.transfer if request_class.nil?
-
request_type_name = "Illumina A #{parent.name}-#{child.name}"
-
RequestType.create!(:name => request_type_name, :key => request_type_name.gsub(/\W+/, '_'), :request_class_name => request_class, :asset_type => 'Well', :order => 1)
-
end
-
1
private :request_type_between
-
-
end
-
-
end
-
-
-
1
['initial_downstream_plate','initial_plate','library_plate','stock_plate'].each do |type|
-
4
require "#{Rails.root.to_s}/app/models/pulldown/#{type}_purpose"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
module Pulldown::Requests
-
1
module BaitLibraryRequest
-
1
def self.included(base)
-
2
base.class_eval do
-
2
fragment_size_details(100, 400)
-
end
-
2
base::Metadata.class_eval do
-
2
include Pulldown::Requests::BaitLibraryRequest::BaitMetadata
-
end
-
end
-
-
# Ensure that the bait library information is also included in the pool information.
-
1
def update_pool_information(pool_information)
-
super
-
pool_information[:bait_library] = request_metadata.bait_library
-
end
-
-
1
module BaitMetadata
-
1
def self.included(base)
-
2
base.class_eval do
-
2
include BaitLibrary::Associations
-
2
association(:bait_library, :name, :scope => :visible)
-
2
validates_presence_of :bait_library
-
2
validate :bait_library_valid
-
end
-
end
-
-
1
def bait_library_valid
-
errors.add(:bait_library_id, "Validation failed: Bait library is no longer available.") unless bait_library.visible?
-
end
-
1
private :bait_library_valid
-
end
-
end
-
-
1
class LibraryCreation < Request::LibraryCreation
-
-
end
-
-
1
class WgsLibraryRequest < LibraryCreation
-
1
fragment_size_details(300, 500)
-
end
-
-
1
class ScLibraryRequest < LibraryCreation
-
1
include BaitLibraryRequest
-
end
-
-
1
class IscLibraryRequest < LibraryCreation
-
1
include BaitLibraryRequest
-
1
include PreCapturePool::Poolable
-
-
1
Metadata.class_eval do
-
1
attribute(:pre_capture_plex_level, :default => 8, :integer => true)
-
end
-
-
1
def update_pool_information(pool_information)
-
super
-
pool_information[:request_type] = request_type.key
-
end
-
-
end
-
-
1
class IscLibraryRequestPart < IscLibraryRequest
-
1
include IlluminaHtp::Requests::LibraryCompletion::FailUpstream
-
end
-
-
1
class StockToCovaris < TransferRequest
-
1
include TransferRequest::InitialTransfer
-
end
-
-
1
class PcrXpToIscLibPool < TransferRequest
-
# This is a legacy state machine
-
1
include IlluminaHtp::Requests::InitialDownstream
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :nx_in_progress
-
1
aasm_state :passed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:nx_in_progress, :failed, :started, :pending] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed] end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
# Specialised implementation of the plate purpose for the stock plates that lead into the various
-
# pulldown pipelines.
-
1
class Pulldown::StockPlatePurpose < PlatePurpose
-
1
include PlatePurpose::Stock
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownAliquotPlate < PulldownPlate
-
1
self.prefix = "FA"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownAliquotPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownEnrichmentFourPlate < PulldownPlate
-
1
self.prefix = "FM"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownEnrichmentFourPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownEnrichmentOnePlate < PulldownPlate
-
1
self.prefix = "FG"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownEnrichmentOnePlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownEnrichmentThreePlate < PulldownPlate
-
1
self.prefix = "FK"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownEnrichmentThreePlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownEnrichmentTwoPlate < PulldownPlate
-
1
self.prefix = "FI"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownEnrichmentTwoPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownLibraryCreationPipeline < LibraryCreationPipeline
-
1
def pulldown?
-
true
-
end
-
-
1
def prints_a_worksheet_per_task?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class PulldownMultiplexLibraryPreparationPipeline < Pipeline
-
1
INBOX_PARTIAL = 'group_by_parent'
-
1
ALWAYS_SHOW_RELEASE_ACTIONS = true
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class PulldownMultiplexedLibraryCreationRequest < CustomerRequest
-
# override default behavior to not copy the aliquots
-
1
def on_started
-
end
-
1
def valid_request_for_pulldown_report?
-
well = self.asset
-
return false if well.nil? || ! well.is_a?(Well)
-
return false if well.plate.nil? || well.map.nil?
-
return false if well.primary_aliquot.nil?
-
return false if well.primary_aliquot.study.nil?
-
return false if well.parent.nil? || ! well.parent.is_a?(Well)
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownMultiplexedLibraryTube < Tube
-
1
include Api::PulldownMultiplexedLibraryTubeIO::Extensions
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownPcrPlate < PulldownPlate
-
1
self.prefix = "FQ"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownPcrPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class PulldownPlate < Plate
-
1
def self.initialize_child_plates
-
#FIXME: refactor to make PulldownPlate.count work
-
PulldownAliquotPlate
-
PulldownSonicationPlate
-
PulldownRunOfRobotPlate
-
PulldownEnrichmentOnePlate
-
PulldownEnrichmentTwoPlate
-
PulldownEnrichmentThreePlate
-
PulldownEnrichmentFourPlate
-
PulldownSequenceCapturePlate
-
PulldownPcrPlate
-
PulldownQpcrPlate
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownQpcrPlate < PulldownPlate
-
1
self.prefix = "FS"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownQpcrPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownRunOfRobotPlate < PulldownPlate
-
1
self.prefix = "FE"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownRunOfRobotPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownSequenceCapturePlate < PulldownPlate
-
1
self.prefix = "FO"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownSequenceCapturePlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class PulldownSonicationPlate < PulldownPlate
-
1
self.prefix = "FC"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class PulldownSonicationPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
-
1
class Purpose < ActiveRecord::Base
-
1
self.table_name =('plate_purposes')
-
-
1
class Relationship < ActiveRecord::Base
-
1
self.table_name =('plate_purpose_relationships')
-
1
belongs_to :parent, :class_name => 'Purpose'
-
1
belongs_to :child, :class_name => 'Purpose'
-
-
1
belongs_to :transfer_request_type, :class_name => 'RequestType'
-
-
1
scope :with_parent, ->(plate_purpose) { { :conditions => { :parent_id => plate_purpose.id } } }
-
1
scope :with_child, ->(plate_purpose) { { :conditions => { :child_id => plate_purpose.id } } }
-
-
1
module Associations
-
1
def self.included(base)
-
2
base.class_eval do
-
2
has_many :child_relationships, :class_name => 'Purpose::Relationship', :foreign_key => :parent_id, :dependent => :destroy
-
2
has_many :child_purposes, :through => :child_relationships, :source => :child
-
-
2
has_many :parent_relationships, :class_name => 'Purpose::Relationship', :foreign_key => :child_id, :dependent => :destroy
-
2
has_many :parent_purposes, :through => :parent_relationships, :source => :parent
-
end
-
end
-
-
# Returns the transfer request type to use between this purpose and the parent given
-
1
def transfer_request_type_from(parent_purpose)
-
relationship = parent_relationships.find_by_parent_id(parent_purpose.id)
-
raise ActiveRecord::RecordNotFound, "Couldn't find relationship between #{parent_purpose.name} and #{name}" if relationship.nil?
-
relationship.transfer_request_type
-
end
-
end
-
end
-
-
1
include Relationship::Associations
-
1
include Uuid::Uuidable
-
-
1
def source_plate(asset)
-
source_purpose_id.present? ? asset.ancestor_of_purpose(source_purpose_id) : asset.stock_plate
-
end
-
-
# There's a barcode printer type that has to be used to print the labels for this type of plate.
-
1
belongs_to :barcode_printer_type
-
-
1
def barcode_type
-
barcode_printer_type.printer_type_id
-
end
-
-
# Things that are created are often in a default location!
-
1
belongs_to :default_location, :class_name => 'Location'
-
1
has_many :messenger_creators, :inverse_of => :purpose
-
-
1
validates_format_of :name, :with => /^\w[\s\w._-]+\w$/i
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :message => "already in use"
-
1
validates_inclusion_of :barcode_for_tecan, :in => ['ean13_barcode','fluidigm_barcode']
-
-
1
scope :where_is_a?, ->(clazz) { where(:type => [clazz,*clazz.descendants].map(&:name)) }
-
-
1
def target_class
-
target_type.constantize
-
end
-
1
private :target_class
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class QcDecision < ActiveRecord::Base
-
-
1
include Uuid::Uuidable
-
-
1
class QcDecisionQcable < ActiveRecord::Base
-
-
1
self.table_name =('qc_decision_qcables')
-
-
1
belongs_to :qcable
-
1
belongs_to :qc_decision, :inverse_of=>:qc_decision_qcables
-
-
1
validates :qcable, :presence => true
-
1
validates :qc_decision, :presence => true
-
1
validates :decision, :presence => true
-
-
8
validates_inclusion_of :decision, :in => Qcable.aasm_events.map {|i,j| i.to_s }
-
-
1
after_create :make_decision
-
-
1
private
-
-
1
def make_decision
-
qcable.send(:"#{decision}!")
-
end
-
end
-
-
1
belongs_to :user
-
1
belongs_to :lot
-
-
1
has_many :qc_decision_qcables, :class_name => 'QcDecision::QcDecisionQcable', :inverse_of => :qc_decision
-
1
has_many :qcables, :through => :qc_decision_qcables
-
-
1
validates_presence_of :user
-
1
validate :user_has_permission, :if => :user
-
-
1
def decisions=(decisions)
-
self.qc_decision_qcables.build(decisions)
-
end
-
-
1
private
-
-
1
def user_has_permission
-
return true if user.qa_manager?
-
errors.add(:user,'does not have permission to make qc decisions.')
-
false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
class QcFile < ActiveRecord::Base
-
-
extend DbFile::Uploader
-
include Uuid::Uuidable
-
-
module Associations
-
# Adds accessors for named fields and attaches documents to them
-
-
def has_qc_files
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
has_many(:qc_files, {:as => :asset, :dependent => :destroy })
-
-
1
def add_qc_file(file, filename=nil)
-
opts = {:uploaded_data => {:tempfile=>file, :filename=>filename}}
-
opts.merge!(:filename=>filename) unless filename.nil?
-
qc_files.create!(opts) unless file.blank?
-
end
-
-
1
def update_concentrations_from(parser)
-
true
-
end
-
}, __FILE__, line)
-
end
-
end
-
-
belongs_to :asset, :polymorphic => true
-
validates_presence_of :asset
-
-
-
# Handle some of the metadata with this callback
-
before_save :update_document_attributes
-
after_save :store_file_extracted_data, :if => :parser
-
-
# CarrierWave uploader - gets the uploaded_data file, but saves the identifier to the "filename" column
-
has_uploaded :uploaded_data, {:serialization_column => "filename"}
-
-
# Method provided for backwards compatibility
-
def current_data
-
uploaded_data.read
-
end
-
-
def retrieve_file
-
begin
-
uploaded_data.cache!(uploaded_data.file)
-
yield(uploaded_data)
-
ensure
-
# We can't actually delete the cache file here, as the send_file
-
# operation happens asynchronously. Instead we can use:
-
# PolymorphicUploader.clean_cached_files!
-
# This cleans the last 24h worth of files, so should be a daily
-
# cron
-
end
-
end
-
-
private
-
-
def parser
-
@parser ||= Parsers.parser_for(uploaded_data.filename, content_type, current_data)
-
end
-
-
def store_file_extracted_data
-
return if parser.nil?
-
asset.update_concentrations_from(parser)
-
end
-
-
# Save Size/content_type Metadata
-
def update_document_attributes
-
if uploaded_data.present?
-
self.content_type = uploaded_data.file.content_type
-
self.size = uploaded_data.file.size
-
end
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class QcMetric < ActiveRecord::Base
-
-
1
extend QcMetric::QcState
-
-
1
InvalidValue = Class.new(StandardError)
-
-
1
QC_DECISION_TRANSITIONS = {
-
'passed' => 'manually_passed',
-
'manually_passed' => 'manually_passed',
-
'failed' => 'manually_failed',
-
'manually_failed' => 'manually_failed'
-
}
-
-
1
PROCEED_TRANSLATION = {
-
true => 'Y',
-
false => 'N'
-
}
-
-
1
new_state 'passed'
-
1
new_state 'failed', :passed => false
-
1
new_state 'manually_passed', :automatic => false
-
1
new_state 'manually_failed', :passed => false, :automatic => false
-
1
new_state 'unprocessable', :passed => false, :proceedable => false
-
-
1
belongs_to :asset
-
1
belongs_to :qc_report
-
1
has_one :product_criteria, :through => :qc_report
-
1
validates_presence_of :asset, :qc_report
-
1
validates_inclusion_of :qc_decision, :in => QcMetric.valid_states
-
-
1
serialize :metrics
-
-
1
scope :with_asset_ids, ->(ids) { where(:asset_id=>ids) }
-
-
1
scope :for_product, ->(product) {
-
joins(:qc_report=>:product_criteria).
-
where(:product_criteria => { :product_id => product})
-
}
-
-
1
scope :stock_metric, ->() {
-
joins(:qc_report=>:product_criteria).
-
where(:product_criteria => { :stage => ProductCriteria::STAGE_STOCK })
-
}
-
-
1
scope :most_recent_first, ->() { order('created_at DESC, id DESC') }
-
-
# Update the new state as appropriate:
-
# - Don't change the state if we already match
-
# - If we have an automatic state, update to a manual state
-
# - If we already have a manual state, perform some magic to ensure eg.
-
# pass -> manual_fail -> pass BUT
-
# unprocessable -> manual_fail -> manual_pass
-
1
def manual_qc_decision=(decision)
-
return if qc_decision == decision
-
return self.qc_decision = decision_to_manual_state(decision) if qc_automatic?
-
return self.qc_decision = decision if original_qc_decision == decision
-
self.qc_decision = decision_to_manual_state(decision)
-
end
-
-
1
def human_proceed
-
PROCEED_TRANSLATION[proceed]
-
end
-
-
1
def human_proceed=(h_proceed)
-
return self.proceed = nil if h_proceed.blank?
-
self.proceed = proceedable? && human_to_bool(PROCEED_TRANSLATION,h_proceed.upcase)
-
end
-
-
# The metric indicates that the sample has been progressed despite poor quality
-
# || false ensures nil gets converted to a boolean
-
1
def poor_quality_proceed
-
(qc_failed? && proceed) || false
-
end
-
-
1
def qc_passed?
-
qc_state_object.passed
-
end
-
-
1
def qc_failed?
-
!qc_passed?
-
end
-
-
1
def proceedable?
-
qc_state_object.proceedable
-
end
-
-
1
def qc_automatic?
-
qc_state_object.automatic
-
end
-
-
1
def original_qc_decision
-
qc_report.original_qc_decision(metrics)
-
end
-
-
1
private
-
-
1
def qc_state_object
-
QcMetric.qc_state_object_called(qc_decision)
-
end
-
-
1
def decision_to_manual_state(decision)
-
hash = QC_DECISION_TRANSITIONS
-
hash[decision].tap do |v|
-
raise(InvalidValue, value_error_message(decision,hash.keys)) if v.nil?
-
end
-
end
-
-
1
def human_to_bool(hash,choice)
-
hash.key(choice).tap do |v|
-
raise(InvalidValue, value_error_message(choice,hash.values)) if v.nil?
-
end
-
end
-
-
1
def value_error_message(decision, accepted_list)
-
accepted = accepted_list.keys.to_sentence(:last_word_connector=>', or ',:two_words_connector=>' or ')
-
"#{decision} is not an acceptable decision. Should be #{accepted}."
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module QcMetric::QcState
-
-
1
State = Struct.new(:name,:automatic,:passed,:proceedable)
-
-
1
def new_state(name,options={})
-
5
@states ||= {}
-
5
@states[name] = State.new(name,options.fetch(:automatic,true),options.fetch(:passed,true),options.fetch(:proceedable,true))
-
end
-
-
1
def valid_states
-
1
@states.keys
-
end
-
-
1
def qc_state_object_called(name)
-
@states[name]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
class QcMetricRequest < ActiveRecord::Base
-
1
belongs_to :qc_metric
-
1
belongs_to :request
-
1
validates_presence_of :request, :qc_metric
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class QcPipeline < Pipeline
-
1
INBOX_PARTIAL='show_qc'
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
1
def qc?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class QcPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
class QcReport < ActiveRecord::Base
-
-
# :id => The primary key for internal use only
-
# :report_identifier => A unique identifier exposed to customers
-
# :state => Tracks report processing and return
-
-
1
include AASM
-
-
1
module StateMachine
-
-
1
module ClassMethods
-
1
def available_states
-
QcReport.aasm_states.map {|state| state.name.to_s }
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
-
# When adding new states, please make sure you update the config/locals/en.yml file
-
# with decriptions.
-
-
1
aasm_column :state
-
-
# A report has just been created and is awaiting processing. There is probably a corresponding delayed job
-
1
aasm_state :queued
-
-
# A report has failed one or more times. Generally this means there is a problem.
-
1
aasm_state :requeued
-
-
# The report has been picked up by the delayed job. Entry into this state triggers building.
-
1
aasm_state :generating, :after_enter => :generate_report
-
-
# The report has been generated and is awaiting customer feedback
-
1
aasm_state :awaiting_proceed
-
-
# Customer feedback has been uploaded. This is generally an end state, but a report can be re-uploaded
-
# at a later date if necessary.
-
1
aasm_state :complete
-
-
# Triggered automatically on after_create. This event is handled by delayed_job
-
# Call generate_without_delay! to bypass delayed job, although make sure there aren't
-
# any existing jobs first.
-
1
aasm_event :generate do
-
1
transitions :from => [:queued,:requeued], :to => :generating
-
end
-
-
# Called on report failure. Generally the delayed job will cycle it through a few times
-
# but most reports in this state will require manual intervention.
-
1
aasm_event :requeue do
-
1
transitions :from => :generating, :to => :requeued
-
end
-
-
# Called automatically when a report is generated
-
1
aasm_event :generation_complete do
-
1
transitions :from => :generating, :to => :awaiting_proceed
-
end
-
-
# A QC report might be uploaded multiple times
-
1
aasm_event :proceed_decision do
-
1
transitions :from => [:complete,:awaiting_proceed], :to => :complete
-
end
-
-
1
aasm_initial_state :queued
-
-
1
def available?
-
awaiting_proceed? or complete?
-
end
-
-
1
extend ClassMethods
-
-
end
-
-
end
-
end
-
-
1
module ReportBehaviour
-
# Generates the report.
-
# Generally speaking this gets triggered automatically, and is handled by the delayed job.
-
# Briefly, an after_create event creates a delayed job to call generate! on the report.
-
# This transitions the report into 'generating' and triggers this event.
-
# On completion the report automatically passes into 'awaiting_proceed' through generation_complete!
-
# If you ever need to trigger the manual building of a report, use: generate_without_delay!
-
# This bypasses the delayed job, but ensures that the state machine is obeyed.
-
1
def generate_report
-
begin
-
study.each_well_for_qc_report_in_batches(exclude_existing,product_criteria) do |assets|
-
# This transaction is inside the block as otherwise large reports experience issues
-
# with high memory usage. In the event that an exception is raised the most
-
# recent set of decisions will be rolled back, and the report will be re-queued.
-
# The rescue event clears out the remaining metrics, this avoids the risk of duplicate
-
# metric on complete reports (Although wont help if, say, the database connection fails)
-
ActiveRecord::Base.transaction do
-
assets.each do |asset|
-
criteria = product_criteria.assess(asset)
-
QcMetric.create!(:asset=>asset,:qc_decision=>criteria.qc_decision,:metrics=>criteria.metrics,:qc_report=>self)
-
end
-
end
-
end
-
generation_complete!
-
-
rescue => e
-
# If something goes wrong, requeue the report and re-raise the exception
-
qc_metrics.clear
-
requeue!
-
raise e
-
end
-
end
-
1
private :generate_report
-
end
-
-
1
include StateMachine
-
1
include ReportBehaviour
-
-
1
belongs_to :product_criteria
-
1
has_one :product, :through => :product_criteria
-
1
belongs_to :study
-
1
has_many :qc_metrics
-
-
1
before_validation :generate_report_identifier, :if => :identifier_required?
-
-
1
after_create :generate!
-
-
1
scope :for_report_page, ->(conditions) {
-
order("id desc").
-
where(conditions).
-
joins(:product_criteria)
-
}
-
-
1
validates_presence_of :product_criteria, :study, :state
-
-
1
validates_inclusion_of :exclude_existing, :in => [true, false], :message => 'should be true or false.'
-
-
# Reports are handled asynchronously
-
1
handle_asynchronously :generate
-
1
handle_asynchronously :generate!
-
-
1
def to_param
-
report_identifier
-
end
-
-
1
def product_id
-
product.try(:id)
-
end
-
-
1
def original_qc_decision(metrics)
-
product_criteria.asses(metrics)
-
end
-
-
1
private
-
-
1
def identifier_required?
-
self.report_identifier.nil?
-
end
-
-
# Note: You won't be able to generate two reports for the
-
# same product / study abbreviation combo within one second
-
# of each other.
-
1
def generate_report_identifier
-
return true if study.nil? || product_criteria.nil?
-
rid = [
-
study.abbreviation,
-
product_criteria.product.name,
-
DateTime.now.to_formatted_s(:number)
-
].compact.join('_').downcase.gsub(/[^\w]/,'_')
-
self.report_identifier = rid
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class QcReport::File
-
-
1
ACCEPTED_MIMETYPE = 'text/csv'
-
1
ACCEPTED_EXTENSTION = 'csv'
-
1
FILE_VERSION_KEY = 'Sequencescape QC Report'
-
1
REPORT_ID_KEY = 'Report Identifier'
-
# We set a maximum header size to stop really large documents from getting
-
# read into the header hash.
-
1
MAXIMUM_HEADER_SIZE = 40
-
# The number of lines processed at a time.
-
1
GROUP_SIZE = 400
-
-
1
DataError = Class.new(StandardError)
-
-
1
attr_reader :errors, :filename, :mime_type
-
-
1
def initialize(file,set_decision,filename=nil,mime_type=ACCEPTED_MIMETYPE)
-
@file = file
-
@filename = filename || File.basename(file.path)
-
@mime_type = mime_type
-
@errors = []
-
@set_decision = set_decision
-
end
-
-
# Generate a report
-
1
def process
-
@valid = true
-
return false unless valid?
-
ActiveRecord::Base.transaction do
-
begin
-
each_group_of_decisions do |group|
-
process_group(group)
-
end
-
rescue DataError, QcMetric::InvalidValue => exception
-
invalid(exception.message)
-
raise ActiveRecord::Rollback
-
end
-
end
-
qc_report.proceed_decision! if @valid
-
@valid
-
end
-
-
1
def valid?
-
return invalid("#{filename} was not a csv file") unless is_a_csv?
-
return invalid("#{filename} does not appear to be a qc report file. Make sure the #{FILE_VERSION_KEY} line has not been removed.") unless is_a_report?
-
return invalid("Couldn't find the report #{report_identifier}. Check that the report identifier has not been modified.") unless qc_report
-
true
-
end
-
-
# The report to which the file corresponds
-
1
def qc_report
-
@qc_report ||= QcReport.find_by_report_identifier(report_identifier)
-
end
-
-
# A hash of the header section
-
1
def headers
-
@headers || parse_headers
-
end
-
-
# The report identifier in the header section
-
1
def report_identifier
-
headers[REPORT_ID_KEY]
-
end
-
-
1
private
-
-
# This should ONLY be called after the headers have been read out.
-
# This puts the column headers at the top of the remaining csv file
-
1
def body_csv
-
@body_csv ||= CSV.new(@file,:headers=>:first_row, :header_converters => [:symbol] )
-
end
-
-
1
def each_group_of_decisions
-
while (group = fetch_group) && group.present?
-
yield group
-
end
-
end
-
-
1
def fetch_group
-
{}.tap do |asset_collection|
-
GROUP_SIZE.times do
-
line = body_csv.gets
-
break if line.nil?
-
asset_id = line[:asset_id].strip.to_i
-
asset_collection[asset_id] = process_line(line)
-
end
-
end
-
end
-
-
1
def process_group(group)
-
asset_ids = group.keys
-
assets = qc_report.qc_metrics.with_asset_ids(asset_ids)
-
raise DataError, "Could not find assets #{(asset_ids - assets.map(&:id)).to_sentence}" if asset_ids.count != assets.length
-
assets.each do |metric|
-
metric.human_proceed = group[metric.asset_id][:proceed]
-
metric.manual_qc_decision = group[metric.asset_id][:qc_decision] if @set_decision
-
metric.save!
-
end
-
end
-
-
1
def process_line(line)
-
qc_decision = (line[:qc_decision]||"").strip
-
proceed = (line[:proceed]||"").strip
-
{:qc_decision=>qc_decision, :proceed=>proceed }
-
end
-
-
1
def invalid(message)
-
errors << message
-
@valid = false
-
end
-
-
# We do a bit of rough-and-ready processing before passing things over to FasterCSV
-
# as it should be a bit faster to capture the most common problems (ie. uploading an xls)
-
# The FasterCSV read-mes even indicate that its pretty poor at handling invalid CSVs.
-
1
def is_a_csv?
-
File.extname(filename).gsub('.','') == ACCEPTED_EXTENSTION || mime_type == ACCEPTED_MIMETYPE
-
end
-
-
1
def is_a_report?
-
headers[FILE_VERSION_KEY].present?
-
end
-
-
1
def is_header?(row)
-
row.compact.present?
-
end
-
-
1
def parse_headers
-
headers = {}
-
header_parser = CSV.new(@file)
-
lines_read = 0
-
while (row = header_parser.shift) && is_header?(row) && lines_read < MAXIMUM_HEADER_SIZE
-
headers[row[0]] = (row[1]||'').strip
-
lines_read += 1
-
end
-
invalid('Please make sure there is an empty line before the column headers.') if lines_read >= MAXIMUM_HEADER_SIZE
-
@headers = headers
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
class QcRequest < CustomerRequest
-
1
include Request::HasNoTargetAsset
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class QcTube < MultiplexedLibraryTube
-
delegate :qc_files, :qc_files=, :add_qc_file, :to => :parent
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
##
-
# A Qcable is an element of a lot which must be approved
-
# before it may be used.
-
-
# require 'qcable/state_machine'
-
-
require 'aasm'
-
-
class Qcable < ActiveRecord::Base
-
-
include Uuid::Uuidable
-
include AASM
-
include Qcable::Statemachine
-
-
belongs_to :lot, :inverse_of => :qcables
-
belongs_to :asset
-
belongs_to :qcable_creator, :inverse_of => :qcables
-
-
has_one :stamp_qcable, :inverse_of => :qcable, :class_name => 'Stamp::StampQcable'
-
has_one :stamp, :through => :stamp_qcable
-
-
validates :lot, :asset, :state, :qcable_creator, :presence => true
-
-
before_validation :create_asset!, :on => :create
-
1
-
delegate :bed, :order, :to => :stamp_qcable, :nil => true
-
-
1
scope :include_for_json, -> { includes([:asset,:lot,:stamp,:stamp_qcable]) }
-
-
1
scope :stamped, -> { includes([:stamp_qcable, :stamp]).where('stamp_qcables.id IS NOT NULL').order('stamps.created_at ASC, stamp_qcables.order ASC')}
-
-
# We accept not only an individual barcode but also an array of them. This builds an appropriate
-
# set of conditions that can find any one of these barcodes. We map each of the individual barcodes
-
# to their appropriate query conditions (as though they operated on their own) and then we join
-
# them together with 'OR' to get the overall conditions.
-
1
scope :with_machine_barcode, ->(*barcodes) {
-
query_details = barcodes.flatten.map do |source_barcode|
-
barcode_number = Barcode.number_to_human(source_barcode)
-
prefix_string = Barcode.prefix_from_barcode(source_barcode)
-
barcode_prefix = BarcodePrefix.find_by_prefix(prefix_string)
-
-
if barcode_number.nil? or prefix_string.nil? or barcode_prefix.nil?
-
{ :query => 'FALSE' }
-
else
-
{ :query => '(wam_asset.barcode=? AND wam_asset.barcode_prefix_id=?)', :conditions => [ barcode_number, barcode_prefix.id ] }
-
end
-
end.inject({ :query => ['FALSE'], :conditions => [nil], :joins=>['LEFT JOIN assets AS wam_asset ON qcables.asset_id = wam_asset.id'] }) do |building, current|
-
building.tap do
-
building[:joins] << current[:joins]
-
building[:query] << current[:query]
-
building[:conditions] << current[:conditions]
-
end
-
end
-
-
{
-
:conditions => [ query_details[:query].join(' OR '), *query_details[:conditions].flatten.compact ],
-
:joins => query_details[:joins].compact.uniq
-
}
-
}
-
-
1
def stamp_index
-
return nil if stamp_qcable.nil?
-
lot.qcables.stamped.index(self)
-
end
-
-
1
private
-
-
1
def asset_purpose
-
lot.target_purpose
-
end
-
-
1
def create_asset!
-
return true if lot.nil?
-
self.asset ||= asset_purpose.create!()
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
module Qcable::Statemachine
-
-
1
module ClassMethods
-
# A little more sensitive than the request state machine
-
1
def suggested_transition_between(current, target)
-
aasm_events.select do |name, event|
-
-
event.transitions_from_state(current.to_sym).any? do |transition|
-
transition.options[:allow_automated?] && transition.to == target.to_sym
-
end
-
end.tap do |events|
-
raise StandardError, "No automated transition from #{current.inspect} to #{target.inspect}" unless events.size == 1
-
end.first.first
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
## State machine
-
1
aasm_column :state
-
-
1
aasm_state :created
-
1
aasm_state :pending, :enter => :on_stamp
-
1
aasm_state :failed, :enter => :on_failed
-
1
aasm_state :passed, :enter => :on_passed
-
1
aasm_state :available, :enter => :on_released
-
1
aasm_state :destroyed, :enter => :on_destroyed
-
1
aasm_state :qc_in_progress, :enter => :on_qc
-
1
aasm_state :exhausted, :enter => :on_used
-
-
1
aasm_initial_state Proc.new {|qcable| qcable.default_state }
-
-
# State Machine events
-
1
aasm_event :do_stamp do
-
1
transitions :to => :pending, :from => [ :created ]
-
end
-
-
1
aasm_event :destroy do
-
1
transitions :to => :destroyed, :from => [:pending,:available], :allow_automated? => true
-
end
-
-
1
aasm_event :qc do
-
1
transitions :to => :qc_in_progress, :from => [:pending], :allow_automated? => true
-
end
-
-
1
aasm_event :release do
-
1
transitions :to => :available, :from => [:pending]
-
end
-
-
1
aasm_event :pass do
-
1
transitions :to => :passed, :from => [:qc_in_progress]
-
end
-
-
1
aasm_event :fail do
-
1
transitions :to => :failed, :from => [:qc_in_progress,:pending]
-
end
-
-
1
aasm_event :use do
-
1
transitions :to => :exhausted, :from => [:available], :allow_automated? => true
-
end
-
-
# new version of combinable named_scope
-
1
scope :for_state, ->(state) { { :conditions => { :state => state } } }
-
-
1
scope :available, -> { where(:state => :available) }
-
1
scope :unavailable, -> { where(:state => [:created,:pending,:failed,:passed,:destroyed,:qc_in_progress,:exhausted]) }
-
-
end
-
end
-
-
#--
-
# These are the callbacks that will be made on entry to a given state. This allows
-
# derived classes to override these and add custom behaviour. You are advised to call
-
# super in any method that you override so that they can be stacked.
-
#++
-
1
def on_stamp
-
lot.template.stamp_to(asset)
-
end
-
-
1
def default_state
-
# We validate the presence of lot, however initial state gets called BEFORE we reach validation
-
return :created if lot.nil?
-
asset_purpose.default_state.to_sym || :created
-
end
-
-
1
def on_failed; end
-
1
def on_passed; end
-
1
def on_released; end
-
1
def on_destroyed; end
-
1
def on_qc; end
-
1
def on_used; end
-
-
1
def transition_to(target_state)
-
send("#{self.class.suggested_transition_between(self.state, target_state)}!")
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class QcableCreator < ActiveRecord::Base
-
-
1
include Uuid::Uuidable
-
-
1
belongs_to :user
-
1
belongs_to :lot
-
1
has_many :qcables
-
-
1
validates :user, :presence => true
-
1
validates :lot, :presence => true
-
-
1
attr_accessor :count
-
-
1
after_create :make_qcables!
-
-
1
def make_qcables!
-
lot.qcables.build([{:qcable_creator=>self}]*count).tap do |_|
-
lot.save!
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class QcableLibraryPlatePurpose < PlatePurpose
-
-
1
module ClassBehaviour
-
-
1
def state_of(plate)
-
qcable_for(plate).state
-
end
-
-
1
def transition_to(plate, state, *ignored)
-
assign_library_information_to_wells(plate)
-
end
-
-
1
private
-
-
1
def qcable_for(plate)
-
Qcable.find_by_asset_id(plate.id)
-
end
-
-
# Ensure that the library information within the aliquots of the well is correct.
-
1
def assign_library_information_to_wells(plate)
-
plate.wells.each do |well|
-
library_type, insert_size = 'QA1', Aliquot::InsertSize.new(100,100)
-
-
well.aliquots.each do |aliquot|
-
aliquot.library ||= well
-
aliquot.library_type ||= library_type
-
aliquot.insert_size ||= insert_size
-
aliquot.save!
-
end
-
end
-
end
-
-
end
-
-
1
include ClassBehaviour
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class QcablePlatePurpose < PlatePurpose
-
-
1
module ClassBehaviour
-
-
1
def state_of(plate)
-
qcable_for(plate).state
-
end
-
-
1
def transition_to(plate, state, *ignored)
-
qcable_for(plate).transition_to(state)
-
end
-
-
1
private
-
-
1
def qcable_for(plate)
-
Qcable.find_by_asset_id(plate.id)
-
end
-
end
-
-
1
include ClassBehaviour
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class QcableTubePurpose < Tube::Purpose
-
-
1
include QcablePlatePurpose::ClassBehaviour
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
class ReRequestSubmission < Order
-
1
include Submission::LinearRequestGraph
-
1
include Submission::Crossable
-
-
1
def is_asset_applicable_to_type?(request_type, asset)
-
true
-
end
-
1
private :is_asset_applicable_to_type?
-
-
1
def all_samples_have_accession_numbers?
-
input_asset_has_gone_through_submission_before? or super
-
end
-
1
private :all_samples_have_accession_numbers?
-
-
# Returns true if the input asset is assumed to have passed through a submission before. In that
-
# case we can assume that it has passed through the accession number checks and is therefore
-
# valid this time round.
-
1
def input_asset_has_gone_through_submission_before?
-
@input_asset ||= Asset.find(self.assets.first)
-
[ MultiplexedLibraryTube, LibraryTube ].any? { |root_type| @input_asset.is_a?(root_type) }
-
end
-
1
private :input_asset_has_gone_through_submission_before?
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
1
class ReferenceGenome < ActiveRecord::Base
-
1
extend Attributable::Association::Target
-
1
include Api::ReferenceGenomeIO::Extensions
-
1
include Uuid::Uuidable
-
-
1
has_many :studies
-
1
has_many :samples
-
1
validates_uniqueness_of :name, :message => "of reference genome already present in database", :allow_blank => true
-
1
scope :sorted_by_name , order("name ASC")
-
-
1
module Associations
-
1
def self.included(base)
-
3
base.validates_presence_of :reference_genome_id
-
3
base.validates_numericality_of :reference_genome_id, :greater_than => 0, :message => 'appears to be invalid'
-
3
base.belongs_to :reference_genome
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ReferenceSequenceTask < Task
-
1
class ReferenceSequenceData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && ReferenceSequenceData.new(request)
-
end
-
-
1
def partial
-
"reference_sequence_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_reference_sequence_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_reference_sequence_task(self, params)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Rename::ChangeName
-
1
include Validateable
-
-
1
class ChangeNameError < ::StandardError
-
1
attr_reader :object
-
1
def initialize(object)
-
@object = object
-
end
-
end
-
-
1
InvalidAction = Class.new(ChangeNameError)
-
-
1
attr_accessor :study
-
1
validates_presence_of(:study)
-
-
1
attr_accessor :user
-
-
1
attr_accessor :replace
-
1
validates_presence_of(:replace)
-
1
attr_accessor :with
-
1
validates_presence_of(:with)
-
-
1
attr_accessor :list_samples_to_rename
-
1
attr_accessor :list_assets_to_rename
-
-
-
1
def initialize(attributes)
-
attributes.each { |k,v| self.send(:"#{k}=", v) }
-
end
-
-
1
def sample_rename_absent?
-
self.list_samples_to_rename.nil? || self.list_samples_to_rename.empty?
-
end
-
-
1
def asset_rename_absent?
-
self.list_assets_to_rename.nil? || self.list_assets_to_rename.empty?
-
end
-
-
1
def reload_objects
-
self.study.samples.reload
-
end
-
-
1
def execute!
-
raise InvalidAction, self unless self.valid?
-
perform_rename_action!
-
end
-
-
1
private
-
-
1
def perform_rename_action!
-
begin
-
ActiveRecord::Base.transaction do
-
perform_rename_action_for_sample! unless sample_rename_absent?
-
perform_rename_action_for_asset! unless asset_rename_absent?
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
reload_objects
-
raise InvalidAction, self
-
end
-
end
-
-
1
def perform_rename_action_for_sample!
-
samples_to_rename = self.study.samples.with_name(self.list_samples_to_rename)
-
samples_to_rename.each { |sample| sample.rename_to!(sample.name.gsub(replace, with)) }
-
self.study.comments.create(:description => "Renamed Samples names: " + replace + " to " + with , :user_id => user.id)
-
end
-
-
1
def perform_rename_action_for_asset!
-
asset_to_rename = self.study.assets.with_name(self.list_assets_to_rename)
-
asset_to_rename.each do |asset|
-
new_name = asset.name.gsub(replace, with)
-
asset.update_attributes!(:name => new_name)
-
end
-
self.study.comments.create(:description => "Renamed Asset names: " + replace + " to " + with, :user_id => user.id)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
-
require 'aasm'
-
-
class Request < ActiveRecord::Base
-
include ModelExtensions::Request
-
include Aliquot::DeprecatedBehaviours::Request
-
-
include Api::RequestIO::Extensions
-
-
self.per_page = 500
-
-
include Uuid::Uuidable
-
include AASM
-
include Commentable
-
include Proxyable
-
include StandardNamedScopes
-
include Request::Statemachine
-
extend Request::Statistics
-
include Batch::RequestBehaviour
-
-
extend EventfulRecord
-
has_many_events
-
has_many_lab_events
-
-
self.inheritance_column = "sti_type"
-
-
def self.delegate_validator
-
DelegateValidation::AlwaysValidValidator
-
end
-
-
scope :for_pipeline, ->(pipeline) {
-
-
joins('LEFT JOIN pipelines_request_types prt ON prt.request_type_id=requests.request_type_id' ).
-
where([ 'prt.pipeline_id=?', pipeline.id]).
-
readonly(false)
-
-
}
-
-
def validator_for(request_option)
-
request_type.request_type_validators.find_by_request_option!(request_option.to_s)
-
end
-
-
scope :for_pipeline, ->(pipeline) {
-
{
-
:joins => [ 'LEFT JOIN pipelines_request_types prt ON prt.request_type_id=requests.request_type_id' ],
-
:conditions => [ 'prt.pipeline_id=?', pipeline.id],
-
:readonly => false
-
}
-
}
-
-
scope :for_pooling_of, ->(plate) {
-
submission_ids = plate.all_submission_ids
-
joins =
-
if plate.stock_plate?
-
[ 'INNER JOIN assets AS pw ON requests.asset_id=pw.id' ]
-
else
-
[
-
'INNER JOIN well_links ON well_links.source_well_id=requests.asset_id',
-
'INNER JOIN assets AS pw ON well_links.target_well_id=pw.id AND well_links.type="stock"',
-
]
-
end
-
{
-
:select => 'uuids.external_id AS pool_id, GROUP_CONCAT(DISTINCT pw_location.description ORDER BY pw.map_id ASC SEPARATOR ",") AS pool_into, requests.*',
-
:joins => joins + [
-
'INNER JOIN maps AS pw_location ON pw.map_id=pw_location.id',
-
'INNER JOIN container_associations ON container_associations.content_id=pw.id',
-
'INNER JOIN uuids ON uuids.resource_id=requests.submission_id AND uuids.resource_type="Submission"'
-
],
-
:group => 'requests.submission_id',
-
:conditions => [
-
'requests.sti_type NOT IN (?) AND container_associations.container_id=? AND requests.submission_id IN (?)',
-
[TransferRequest,*TransferRequest.descendants].map(&:name), plate.id, submission_ids
-
]
-
}
-
}
-
-
scope :for_pre_cap_grouping_of, ->(plate) {
-
joins =
-
if plate.stock_plate?
-
[ 'INNER JOIN assets AS pw ON requests.asset_id=pw.id' ]
-
else
-
[
-
'INNER JOIN well_links ON well_links.source_well_id=requests.asset_id',
-
'INNER JOIN assets AS pw ON well_links.target_well_id=pw.id AND well_links.type="stock"',
-
]
-
end
-
-
select('min(uuids.external_id) AS group_id, GROUP_CONCAT(DISTINCT pw_location.description SEPARATOR ",") AS group_into, requests.*').
-
joins(joins + [
-
'INNER JOIN maps AS pw_location ON pw.map_id=pw_location.id',
-
'INNER JOIN container_associations ON container_associations.content_id=pw.id',
-
'INNER JOIN pre_capture_pool_pooled_requests ON requests.id=pre_capture_pool_pooled_requests.request_id',
-
'INNER JOIN uuids ON uuids.resource_id=pre_capture_pool_pooled_requests.pre_capture_pool_id AND uuids.resource_type="PreCapturePool"'
-
]
-
).
-
group('pre_capture_pool_pooled_requests.pre_capture_pool_id').
-
where([
-
'requests.sti_type NOT IN (?) AND container_associations.container_id=? AND requests.state="pending"',
-
[TransferRequest,*TransferRequest.descendants].map(&:name), plate.id
-
])
-
-
}
-
-
scope :for_event_notification_by_order, ->(order) {
-
where([
-
'requests.sti_type NOT IN (?) AND requests.state = "passed" AND requests.order_id=?',
-
[TransferRequest,*TransferRequest.descendants].map(&:name), order.id
-
])
-
}
-
-
-
scope :including_samples_from_target, ->() { includes({ :target_asset => {:aliquots => :sample } }) }
-
scope :including_samples_from_source, ->() { includes({ :asset => {:aliquots => :sample } }) }
-
-
scope :for_order_including_submission_based_requests, ->(order) {
-
# To obtain the requests for an order and the sequencing requests of its submission (as they are defined
-
# as a common element for any order in the submission)
-
where(['requests.order_id=? OR (requests.order_id IS NULL AND requests.submission_id=?)', order.id, order.submission.id])
-
-
}
-
-
belongs_to :pipeline
-
belongs_to :item
-
-
has_many :failures, :as => :failable
-
-
belongs_to :request_type, :inverse_of => :requests
-
delegate :billable?, :to => :request_type, :allow_nil => true
-
belongs_to :workflow, :class_name => "Submission::Workflow"
-
-
scope :for_billing, -> { includes([ :initial_project, :request_type, { :target_asset => :aliquots }]) }
-
-
belongs_to :user
-
belongs_to :request_purpose
-
validates_presence_of :request_purpose
-
-
belongs_to :submission, :inverse_of => :requests
-
belongs_to :submission_pool, :foreign_key => :submission_id
-
-
belongs_to :order, :inverse_of => :requests
-
-
has_many :submission_siblings, :through => :submission, :source => :requests, :class_name => 'Request', :conditions => {:request_type_id => '#{request_type_id}'}
-
-
scope :with_request_type_id, ->(id) { { :conditions => { :request_type_id => id } } }
-
-
scope :for_pacbio_sample_sheet, -> { includes([{:target_asset=>:map},:request_metadata]) }
-
-
has_many :qc_metric_requests
-
has_many :qc_metrics, :through => :qc_metric_requests
-
-
# project is read only so we can set it everywhere
-
# but it will be only used in specific and controlled place
-
belongs_to :initial_project, :class_name => "Project"
-
-
has_many :request_events, :order => 'current_from ASC'
-
def current_request_event
-
request_events.current.last
-
end
-
-
def project_id=(project_id)
-
raise RuntimeError, "Initial project already set" if initial_project_id
-
self.initial_project_id = project_id
-
end
-
-
-
def submission_plate_count
-
submission.requests.find(:all,
-
:conditions=>{:request_type_id=>request_type_id},
-
:joins=>'LEFT JOIN container_associations AS spca ON spca.content_id = requests.asset_id',
-
:group=>'spca.container_id'
-
).count
-
end
-
-
def update_responsibilities!
-
# Do nothing
-
end
-
-
-
def project=(project)
-
return unless project
-
self.project_id=project.id
-
end
-
-
#same as project with study
-
belongs_to :initial_study, :class_name => "Study"
-
-
def study_id=(study_id)
-
raise RuntimeError, "Initial study already set" if initial_study_id
-
self.initial_study_id = study_id
-
end
-
-
def study=(study)
-
return unless study
-
self.study_id=study.id
-
end
-
-
def associated_studies
-
return [initial_study] if initial_study.present?
-
return asset.studies.uniq if asset.present?
-
return submission.studies if submission.present?
-
[]
-
end
-
-
-
scope :between, ->(source,target) { where(:asset_id => source.id, :target_asset_id => target.id) }
-
scope :into_by_id, ->(target_ids) { where(:target_asset_id => target_ids ) }
-
-
scope :request_type, ->(request_type) {
-
where(:request_type_id => request_type)
-
}
-
-
scope :where_is_a?, ->(clazz) { where([ 'sti_type IN (?)', [ clazz, *clazz.descendants ].map(&:name) ]) }
-
scope :where_is_not_a?, ->(clazz) { where([ 'sti_type NOT IN (?)', [ clazz, *clazz.descendants ].map(&:name) ]) }
-
scope :where_has_a_submission, -> { where('submission_id IS NOT NULL') }
-
-
scope :full_inbox, -> { where(:state => ["pending","hold"]) }
-
-
scope :with_asset, -> { where('asset_id is not null')}
-
scope :with_target, -> { where('target_asset_id is not null and (target_asset_id <> asset_id)')}
-
scope :join_asset, -> { joins(:asset)}
-
scope :with_asset_location, -> { includes(:asset => :map) }
-
-
scope :siblings_of, ->(request) { { :conditions => ['asset_id = ? AND NOT id = ?', request.asset_id, request.id ] } }
-
-
#Asset are Locatable (or at least some of them)
-
belongs_to :location_association, :primary_key => :locatable_id, :foreign_key => :asset_id
-
scope :located, ->(location_id) { joins(:location_association).where(['location_associations.location_id = ?', location_id ]).readonly(false) }
-
-
#Use container location
-
scope :holder_located, ->(location_id) {
-
joins(["INNER JOIN container_associations hl ON hl.content_id = asset_id", "INNER JOIN location_associations ON location_associations.locatable_id = hl.container_id"]).
-
where(['location_associations.location_id = ?', location_id ]).
-
readonly(false)
-
}
-
-
scope :holder_not_control, -> {
-
joins(["INNER JOIN container_associations hncca ON hncca.content_id = asset_id", "INNER JOIN assets AS hncc ON hncc.id = hncca.container_id"]).
-
where(['hncc.sti_type != ?', 'ControlPlate' ]).
-
readonly(false)
-
}
-
scope :without_asset, -> { where('asset_id is null') }
-
scope :without_target, -> { where('target_asset_id is null') }
-
scope :excluding_states, ->(states) {
-
where([states.map{|s| '(state != ?)' }.join(" OR "), states].flatten)
-
}
-
scope :ordered, -> { order("id ASC") }
-
scope :full_inbox, -> { where(:state => ["pending","hold"]) }
-
scope :hold, -> { where(:state => "hold") }
-
-
scope :loaded_for_inbox_display, -> { includes([{:submission => {:orders =>:study}, :asset => [:scanned_into_lab_event,:studies]}])}
-
scope :loaded_for_grouped_inbox_display, -> { includes([ {:submission => :orders}, :asset , :target_asset, :request_type ])}
-
-
scope :ordered_for_ungrouped_inbox, -> { order('id DESC') }
-
scope :ordered_for_submission_grouped_inbox, -> { order('submission_id DESC, id ASC') }
-
-
scope :group_conditions, ->(conditions, variables) {
-
where([ conditions.join(' OR '), *variables ])
-
}
-
-
def self.group_requests(finder_method, options = {})
-
target = options[:by_target] ? 'target_asset_id' : 'asset_id'
-
-
send(finder_method, options.slice(:group).merge(
-
:select => "DISTINCT requests.*, tca.container_id AS container_id, tca.content_id AS content_id",
-
:joins => "INNER JOIN container_associations tca ON tca.content_id=#{target}",
-
:readonly => false,
-
:include => :request_metadata
-
))
-
end
-
-
scope :for_submission_id, ->(id) { { :conditions => { :submission_id => id } } }
-
scope :for_asset_id, ->(id) { { :conditions => { :asset_id => id } } }
-
scope :for_study_ids, ->(ids) {
-
select('DISTINCT requests.*').
-
joins('INNER JOIN aliquots AS al ON requests.asset_id = al.receptacle_id').
-
where(['al.study_id IN (?)',ids])
-
}
-
-
scope :for_study_id, ->(id) { for_study_ids(id) }
-
-
scope :for_group_by, ->(attributes) {
-
group(attributes).
-
select('requests.*, MAX(priority) AS max_priority, hl.container_id AS container_id, count(DISTINCT requests.id) AS request_count')
-
}
-
-
def self.for_study(study)
-
Request.for_study_id(study.id)
-
end
-
def self.for_studies(studies)
-
for_study_ids(studies.map(&:id))
-
end
-
-
scope :for_initial_study_id, ->(id) { { :conditions => {:initial_study_id => id } }
-
}
-
-
-
-
-
delegate :study, :study_id, :to => :asset, :allow_nil => true
-
-
scope :for_workflow, ->(workflow) { { :joins => :workflow, :conditions => { :workflow => { :key => workflow } } } }
-
scope :for_request_types, ->(types) { { :joins => :request_type, :conditions => { :request_types => { :key => types } } } }
-
-
scope :for_search_query, ->(query,with_includes) {
-
where([ 'id=?', query ])
-
}
-
-
scope :find_all_target_asset, ->(target_asset_id) {
-
where(['target_asset_id = ?', "#{target_asset_id}" ])
-
}
-
scope :for_studies, ->(*studies) {
-
where(:initial_study_id => studies.map(&:id))
-
}
-
-
scope :with_assets_for_starting_requests, -> { includes([:request_metadata,{:asset=>:aliquots,:target_asset=>:aliquots}]) }
-
scope :not_failed, -> { where(['state != ?', 'failed']) }
-
-
#------
-
#TODO: use eager loading association
-
def self.get_holder_asset_id_map(request_ids)
-
# the alias request_id to id is a trick to store request_id in a existing attribute of Asset.
-
rows = ContainerAssociation.find(:all, :joins => "INNER JOIN requests ON content_id = asset_id" , :select => "requests.id id, container_id", :conditions => ["requests.id IN (?)", request_ids])
-
# now , we transform the result into a Hash : request_ids -> holder id
-
h = {}
-
rows.each do |row|
-
h[row.id] = row.container_id
-
end
-
-
return h
-
end
-
-
def self.get_target_holder_asset_id_map(request_ids)
-
# the alias request_id to id is a trick to store request_id in a existing attribute of Asset.
-
rows = ContainerAssociation.find(:all, :joins => "INNER JOIN requests ON content_id = target_asset_id" , :select => "requests.id id, container_id", :conditions => ["requests.id IN (?)", request_ids])
-
# now , we transform the result into a Hash : request_ids -> holder id
-
h = {}
-
rows.each do |row|
-
h[row.id] = row.container_id
-
end
-
-
return h
-
end
-
-
# The options that are required for creation. In other words, the truly required options that must
-
# be filled in and cannot be changed if the asset we're creating is used downstream. For example,
-
# a library tube definitely has to have fragment_size_required_from, fragment_size_required_to and
-
# library_type and these cannot be changed once the library has been made.
-
#
-
#--
-
# Side note: really this information should be stored on the asset itself, which suggests there is
-
# a discrepancy in our model somewhere.
-
#++
-
def request_options_for_creation
-
{}
-
end
-
-
def get_value(request_information_type)
-
return '' unless self.request_metadata.respond_to?(request_information_type.key.to_sym)
-
value = self.request_metadata.send(request_information_type.key.to_sym)
-
return value.to_s if value.blank? or request_information_type.data_type != 'Date'
-
return value.to_date.strftime('%d %B %Y')
-
end
-
-
def value_for(name, batch = nil)
-
rit = RequestInformationType.find_by_name(name)
-
rit_value = self.get_value(rit) if rit.present?
-
return rit_value if rit_value.present?
-
-
list = (batch.present? ? self.lab_events_for_batch(batch) : self.lab_events)
-
list.each { |event| desc = event.descriptor_value_for(name) and return desc }
-
""
-
end
-
-
def has_passed(batch, task)
-
self.lab_events_for_batch(batch).any? { |event| event.description == task.name }
-
end
-
-
def lab_events_for_batch(batch)
-
self.lab_events.find_all_by_batch_id(batch.id)
-
end
-
-
def event_with_key_value(k, v = nil)
-
v.nil? ? false : self.lab_events.with_descriptor(k, v).first
-
end
-
-
# This is used for the default next or previous request check. It means that if the caller does not specify a
-
# block then we can use this one in its place.
-
PERMISSABLE_NEXT_REQUESTS = Object.new.tap do |permission|
-
def permission.call(request)
-
request.pending? or request.blocked?
-
end
-
end
-
-
def next_requests(pipeline, &block)
-
#TODO remove pipeline parameters
-
# we filter according to the next pipeline
-
next_pipeline = pipeline.next_pipeline
-
#return [] if next_pipeline.nil?
-
-
block ||= PERMISSABLE_NEXT_REQUESTS
-
eligible_requests = if target_asset.present?
-
target_asset.requests
-
else
-
return [] if submission.nil?
-
submission.next_requests(self)
-
end
-
-
eligible_requests.select do |r|
-
( next_pipeline.nil? or
-
next_pipeline.request_types_including_controls.include?(r.request_type)
-
) and block.call(r)
-
end
-
end
-
-
def target_tube
-
target_asset if target_asset.is_a?(Tube)
-
end
-
-
def previous_failed_requests
-
self.asset.requests.select { |previous_failed_request| (previous_failed_request.failed? or previous_failed_request.blocked?)}
-
end
-
-
def add_comment(comment, user)
-
self.comments.create({:description => comment, :user => user})
-
end
-
-
def self.number_expected_for_submission_id_and_request_type_id(submission_id, request_type_id)
-
Request.count(:conditions => "submission_id = #{submission_id} and request_type_id = #{request_type_id}")
-
end
-
-
def return_pending_to_inbox!
-
raise StandardError, "Can only return pending requests, request is #{state}" unless pending?
-
remove_unused_assets
-
end
-
-
def remove_unused_assets
-
ActiveRecord::Base.transaction do
-
return if target_asset.nil?
-
self.target_asset.ancestors.clear
-
self.target_asset.destroy
-
self.save!
-
end
-
end
-
-
def format_qc_information
-
return [] if self.lab_events.empty?
-
-
self.events.map do |event|
-
next if event.family.nil? or not [ 'pass', 'fail' ].include?(event.family.downcase)
-
-
message = event.message || "(No message was specified)"
-
{"event_id" => event.id, "status" => event.family.downcase, "message" => message, "created_at" => event.created_at}
-
end.compact
-
end
-
-
def copy
-
RequestFactory.copy_request(self)
-
end
-
-
def cancelable?
-
self.batch_request.nil? && (pending? || blocked?)
-
end
-
-
def update_priority
-
priority = (self.priority + 1) % 4
-
submission.update_attributes!(:priority => priority)
-
end
-
-
def priority
-
submission.try(:priority)||0
-
end
-
-
def request_type_updatable?(new_request_type)
-
self.pending?
-
end
-
-
def customer_accepts_responsibility!
-
# Do nothing
-
end
-
-
extend Metadata
-
has_metadata do
-
-
end
-
-
# NOTE: With properties Request#name would have been silently sent through to the property. With metadata
-
1
# we now need to be explicit in how we want it delegated.
-
delegate :name, :to => :request_metadata
-
-
# Adds any pool information to the structure so that it can be reported to client applications
-
1
def update_pool_information(pool_information)
-
# Does not need anything here
-
end
-
-
1
def submission_siblings
-
submission.requests.with_request_type_id(request_type_id)
-
end
-
-
# The date at which the submission was made. In most cases this will be similar to the request's created_at
-
# timestamp. We go via submission to ensure that copied requests bear the original timestamp.
-
1
def submitted_at
-
# Hopefully we shouldn't get any requests that don't have a submission. But validation is turned off, so
-
# we should assume it it possible.
-
return '' if submission.nil?
-
submission.created_at.strftime('%Y-%m-%d')
-
end
-
-
1
def role
-
order.try(:role)
-
end
-
-
1
def self.accessioning_required?
-
false
-
end
-
-
1
def ready?
-
true
-
end
-
-
1
def target_purpose
-
nil
-
end
-
-
1
def library_creation?
-
false
-
end
-
-
1
def manifest_processed!; end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module Request::AccessioningRequired
-
-
1
def accessioning_required?
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
1
class Request::ChangeDecision
-
1
include ::Validateable
-
-
1
class ChangeDecisionError < ::StandardError
-
1
attr_reader :object
-
1
def initialize(object)
-
@object = object
-
end
-
end
-
-
1
InvalidDecision = Class.new(ChangeDecisionError)
-
-
1
attr_accessor :change_decision_check_box
-
1
attr_accessor :asset_qc_state_check_box
-
-
1
def checkboxes
-
[ self.change_decision_check_box, self.asset_qc_state_check_box ]
-
end
-
1
validates_each(:checkboxes) do |record, attribute, list_of_checkbox_values|
-
record.errors.add(attribute, 'at least one must be selected') if list_of_checkbox_values.all? { |v| v.blank? or v == '0' }
-
end
-
-
1
attr_accessor :asset_qc_state
-
1
validates_each(:asset_qc_state, :unless => :asset_qc_state_absent?) do |record, attr, value|
-
if not record.request.target_asset.has_been_through_qc?
-
record.errors.add(:asset, 'has not been through QC')
-
elsif value == record.request.target_asset.qc_state
-
record.errors.add(:asset_qc_state, 'cannot be same as current state')
-
end
-
end
-
1
validates_presence_of :asset_qc_state, :unless => :asset_qc_state_absent?
-
-
1
attr_accessor :comment
-
1
validates_presence_of :comment
-
-
1
attr_accessor :request
-
-
1
attr_accessor :user
-
1
validates_presence_of(:request)
-
-
1
def initialize(attributes)
-
attributes.each { |k,v| self.send(:"#{k}=", v) }
-
end
-
-
1
def state_change?
-
self.change_decision_check_box == "1"
-
end
-
-
1
def asset_qc_state_absent?
-
self.asset_qc_state_check_box == "0" || self.asset_qc_state_check_box.nil?
-
end
-
-
1
def execute!
-
raise InvalidDecision, self unless self.valid?
-
perform_decision_change!
-
end
-
-
1
private
-
-
1
def perform_decision_change!
-
begin
-
ActiveRecord::Base.transaction do
-
perform_decision_change_request_state! if state_change?
-
perform_decision_change_asset_qc_state! unless asset_qc_state_absent?
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
reload_objects
-
raise InvalidDecision, self
-
end
-
end
-
-
1
def reload_objects
-
self.request.reload
-
self.request.target_asset.reload
-
end
-
-
1
def perform_decision_change_request_state!
-
previous_state = self.request.state
-
ActiveRecord::Base.transaction do
-
self.request.change_decision!
-
self.request.events.create!({:message => "Change state from #{previous_state} to #{state}", :created_by => self.user.login, :family => "update"})
-
self.request.comments.create!(:description => self.comment, :user_id => self.user.id)
-
end
-
end
-
-
1
def perform_decision_change_asset_qc_state!
-
previous_state = self.request.target_asset.qc_state
-
self.request.target_asset.set_qc_state(self.asset_qc_state)
-
#self.request.asset.events << Event.new({:message => "Change qc_state from #{previous_state} to #{asset_qc_state}", :created_by => self.user.login, :family => self.asset_qc_state})
-
self.request.target_asset.comments.create!(:description => self.comment, :user_id => self.user.id)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
module Request::CustomerResponsibility
-
1
def self.included(base)
-
7
base::Metadata.class_eval do
-
7
attribute(:customer_accepts_responsibility, :boolean => true)
-
-
7
validate :customer_can_accept_responsibility?, :if => :customer_accepts_responsibility_changed?, :on => :update
-
-
7
def customer_can_accept_responsibility?
-
return true unless request.try(:failed?)
-
self.errors.add(:customer_accepts_responsibility, 'can not be changed once a request is failed.')
-
raise ActiveRecord::RecordInvalid, self
-
end
-
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
module Request::GroupingHelpers
-
1
def group_requests_by_submission_id(requests)
-
# NOTE: Not using group_by(&:submission_id) to maintain the order of the submissions from the order of the requests
-
requests.inject(ActiveSupport::OrderedHash.new { |h,k| h[k] = [] }) do |groups, request|
-
groups.tap { groups[request.submission_id] << request }
-
end.values
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# Some requests, notably DNA QC, do not actually transfer to a target asset but work on the source
-
# one. In this case there are certain things that are not permitted.
-
1
module Request::HasNoTargetAsset
-
1
def on_started
-
# Do not transfer the aliquots as there is no target asset
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
1
class Request::LibraryCreation < CustomerRequest
-
# Override the behaviour of Request so that we do not copy the aliquots from our source asset
-
# to the target when we are passed. This is actually done by the TransferRequest from plate
-
# to plate as it goes through being processed.
-
1
include Request::CustomerResponsibility
-
-
1
def on_started
-
# Override the default behaviour to not do the transfer
-
end
-
-
# Add common pool information, like insert size and library type
-
1
def update_pool_information(pool_information)
-
pool_information.merge!(
-
:insert_size => { :from => insert_size.from, :to => insert_size.to },
-
:library_type => { :name => library_type }
-
)
-
end
-
-
# Convenience helper for ensuring that the fragment size information is properly treated.
-
# The columns in the database are strings and we need them to be integers, hence we force
-
# that here.
-
1
def self.fragment_size_details(minimum = :no_default, maximum = :no_default)
-
7
minimum_details, maximum_details = { :required => true, :integer => true }, { :required => true, :integer => true }
-
7
minimum_details[:default] = minimum unless minimum == :no_default
-
7
maximum_details[:default] = maximum unless maximum == :no_default
-
-
7
class_eval do
-
7
has_metadata :as => Request do
-
# Redefine the fragment size attributes as they are fixed
-
7
attribute(:fragment_size_required_from, minimum_details)
-
7
attribute(:fragment_size_required_to, maximum_details)
-
7
attribute(:gigabases_expected, :positive_float => true)
-
end
-
7
include Request::LibraryManufacture
-
end
-
7
const_get(:Metadata).class_eval do
-
7
def fragment_size_required_from
-
self[:fragment_size_required_from].try(:to_i)
-
end
-
-
7
def fragment_size_required_to
-
self[:fragment_size_required_to].try(:to_i)
-
end
-
end
-
end
-
-
1
def request_options_for_creation
-
Hash[[:fragment_size_required_from, :fragment_size_required_to, :library_type].map { |f| [ f, request_metadata[f] ] }]
-
end
-
-
1
def library_creation?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
# Any request involved in building a library should include this module that defines some of the
-
# most common behaviour, namely the library type and insert size information.
-
1
module Request::LibraryManufacture
-
1
def self.included(base)
-
8
base::Metadata.class_eval do
-
8
attribute(:fragment_size_required_from, :required => true, :integer => true)
-
8
attribute(:fragment_size_required_to, :required => true, :integer => true)
-
8
attribute(:library_type, :required => true, :validator=>true, :selection=>true)
-
end
-
-
8
base.class_eval do
-
8
extend ClassMethods
-
end
-
-
8
base.const_set(:RequestOptionsValidator, Class.new(DelegateValidation::Validator) do
-
8
delegate_attribute :fragment_size_required_from, :fragment_size_required_to, :to => :target, :type_cast => :to_i
-
8
validates_numericality_of :fragment_size_required_from, :integer_only => true, :greater_than => 0
-
8
validates_numericality_of :fragment_size_required_to, :integer_only => true, :greater_than => 0
-
end)
-
end
-
-
1
module ClassMethods
-
1
def delegate_validator
-
self::RequestOptionsValidator
-
end
-
end
-
-
1
def insert_size
-
Aliquot::InsertSize.new(
-
request_metadata.fragment_size_required_from,
-
request_metadata.fragment_size_required_to
-
)
-
end
-
-
1
def library_type
-
request_metadata.library_type
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
class Request::Multiplexing < CustomerRequest
-
-
1
after_create :register_transfer_callback
-
-
1
def register_transfer_callback
-
# We go via order as we need to get a particular instance of submission
-
order.submission.register_callback(:once) do
-
Transfer::FromPlateToTubeByMultiplex.create!(
-
:source => self.asset.plate,
-
:user => self.order.user
-
)
-
end
-
end
-
-
-
1
redefine_state_machine do
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :passed
-
1
aasm_state :failed
-
1
aasm_state :cancelled
-
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed] end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
# This is a module containing the standard statemachine for a request that needs it.
-
# It provides various callbacks that can be hooked in to by the derived classes.
-
1
require 'aasm'
-
-
1
module Request::Statemachine
-
1
COMPLETED_STATE = [ 'passed', 'failed' ]
-
1
OPENED_STATE = [ 'pending', 'blocked', 'started' ]
-
1
ACTIVE = QUOTA_COUNTED = [ 'passed', 'pending', 'blocked', 'started' ]
-
1
INACTIVE = QUOTA_EXEMPTED = [ 'failed', 'cancelled', 'aborted' ]
-
-
1
module ClassMethods
-
1
def redefine_state_machine(&block)
-
# Destroy all evidence of the statemachine we've inherited! Ugly, but it works!
-
11
instance_variable_set(:@aasm, nil)
-
11
AASM::StateMachine[self] = AASM::StateMachine.new('')
-
11
instance_eval(&block)
-
end
-
-
# Determines the most likely event that should be fired when transitioning between the two states. If there is
-
# only one option then that is what is returned, otherwise an exception is raised.
-
1
def suggested_transition_between(current, target)
-
aasm_events.select do |name, event|
-
event.transitions_from_state(current.to_sym).any? do |transition|
-
transition.to == target.to_sym
-
end
-
end.tap do |events|
-
raise StandardError, "No obvious transition from #{current.inspect} to #{target.inspect}" unless events.size == 1
-
end.first.first
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
-
## State machine
-
1
aasm_column :state
-
1
aasm_state :pending
-
1
aasm_state :started, :after_enter => :on_started
-
1
aasm_state :failed, :after_enter => :on_failed
-
1
aasm_state :passed, :after_enter => :on_passed
-
1
aasm_state :cancelled, :after_enter => :on_cancelled
-
1
aasm_state :blocked, :after_enter => :on_blocked
-
1
aasm_state :hold, :after_enter => :on_hold
-
1
aasm_initial_state :pending
-
-
1
aasm_event :hold do
-
1
transitions :to => :hold, :from => [ :pending ]
-
end
-
-
# State Machine events
-
1
aasm_event :start do
-
1
transitions :to => :started, :from => [:pending, :hold]
-
end
-
-
1
aasm_event :pass do
-
1
transitions :to => :passed, :from => [:started]
-
end
-
-
1
aasm_event :fail do
-
1
transitions :to => :failed, :from => [:started]
-
end
-
-
1
aasm_event :retrospective_pass do
-
1
transitions :to => :passed, :from => [:failed]
-
end
-
-
1
aasm_event :retrospective_fail do
-
1
transitions :to => :failed, :from => [:passed]
-
end
-
-
1
aasm_event :block do
-
1
transitions :to => :blocked, :from => [:pending]
-
end
-
-
1
aasm_event :unblock do
-
1
transitions :to => :pending, :from => [:blocked]
-
end
-
-
1
aasm_event :detach do
-
1
transitions :to => :pending, :from => [:cancelled]
-
end
-
-
1
aasm_event :reset do
-
1
transitions :to => :pending, :from => [:hold]
-
end
-
-
1
aasm_event :cancel do
-
1
transitions :to => :cancelled, :from => [:started, :hold]
-
end
-
-
1
aasm_event :return do
-
1
transitions :to => :pending, :from => [:failed, :passed]
-
end
-
-
1
aasm_event :cancel_completed do
-
1
transitions :to => :cancelled, :from => [:failed, :passed]
-
end
-
-
1
aasm_event :cancel_from_upstream do
-
1
transitions :to => :cancelled, :from => [:pending]
-
end
-
-
1
aasm_event :cancel_before_started do
-
1
transitions :to => :cancelled, :from => [:pending, :hold]
-
end
-
-
1
aasm_event :submission_cancelled do
-
1
transitions :to => :cancelled, :from => [:pending,:cancelled]
-
end
-
-
1
aasm_event :fail_from_upstream do
-
1
transitions :to => :cancelled, :from => [:pending]
-
1
transitions :to => :failed, :from => [:started]
-
1
transitions :to => :failed, :from => [:passed]
-
end
-
-
1
scope :for_state, ->(state) { where(:state => state) }
-
-
1
scope :completed, -> { where(:state => COMPLETED_STATE) }
-
1
scope :passed, -> { where(:state => "passed") }
-
1
scope :failed, -> { where(:state => "failed") }
-
1
scope :pipeline_pending, -> { where(:state => "pending") } # we don't want the blocked one here }
-
1
scope :pending, -> { where(:state => ["pending", "blocked"]) } # block is a kind of substate of pending }
-
-
1
scope :started, -> { where(:state => "started") }
-
1
scope :cancelled, -> { where(:state => "cancelled") }
-
1
scope :aborted, -> { where(:state => "aborted") }
-
-
1
scope :opened, -> { where(:state => OPENED_STATE) }
-
1
scope :closed, -> { where(:state => ["passed", "failed", "cancelled", "aborted"]) }
-
1
scope :hold, -> { where(:state => "hold") }
-
end
-
end
-
-
#--
-
# These are the callbacks that will be made on entry to a given state. This allows
-
# derived classes to override these and add custom behaviour. You are advised to call
-
# super in any method that you override so that they can be stacked.
-
#++
-
-
# On starting a request the aliquots are copied from the source asset to the target
-
# and updated with the project and study information from the request itself.
-
1
def on_started
-
transfer_aliquots
-
end
-
-
1
def transfer_aliquots
-
target_asset.aliquots << asset.aliquots.map do |aliquot|
-
aliquot.dup.tap do |clone|
-
clone.study_id = initial_study_id || aliquot.study_id
-
clone.project_id = initial_project_id || aliquot.project_id
-
end
-
end
-
end
-
-
1
def change_decision!
-
Rails.logger.warn('Change decision is being deprecated in favour of retrospective_pass and retrospective_fail!')
-
return retrospective_fail! if passed?
-
return retrospective_pass! if failed?
-
raise StandardError, "Can only use change decision on passed or failed requests"
-
end
-
1
deprecate :change_decision!
-
-
1
def on_failed
-
-
end
-
-
1
def on_passed
-
-
end
-
-
1
def on_cancelled
-
-
end
-
-
1
def on_blocked
-
-
end
-
-
1
def on_hold
-
-
end
-
-
1
def failed_upstream!
-
fail_from_upstream! unless failed?
-
end
-
-
1
def failed_downstream!
-
# Do nothing by default
-
end
-
-
1
def finished?
-
self.passed? || self.failed?
-
end
-
-
1
def terminated?
-
self.failed? || self.cancelled?
-
end
-
-
1
def closed?
-
["passed", "failed", "cancelled", "aborted"].include?(self.state)
-
end
-
-
1
def open?
-
["pending", "started"].include?(self.state)
-
end
-
-
1
def cancellable?
-
["pending", "cancelled"].include?(self.state)
-
end
-
-
1
def transition_to(target_state)
-
send("#{self.class.suggested_transition_between(self.state, target_state)}!")
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
module Request::Statistics
-
module DeprecatedMethods
-
# TODO - Move these to named scope on Request
-
def total_requests(request_type)
-
self.requests.request_type(request_type).count(:id, :distinct=>true)
-
end
-
-
def completed_requests(request_type)
-
self.requests.request_type(request_type).completed.count(:id, :distinct=>true)
-
end
-
-
def passed_requests(request_type)
-
self.requests.request_type(request_type).passed.count(:id, :distinct=>true)
-
end
-
-
def failed_requests(request_type)
-
self.requests.request_type(request_type).failed.count(:id, :distinct=>true)
-
end
-
-
def pending_requests(request_type)
-
self.requests.request_type(request_type).pending.count(:id, :distinct=>true)
-
end
-
-
def started_requests(request_type)
-
self.requests.request_type(request_type).started.count(:id, :distinct=>true)
-
end
-
-
def cancelled_requests(request_type)
-
# distinct.count(:id) in rails_4
-
self.requests.request_type(request_type).cancelled.count(:id, :distinct=>true)
-
end
-
def total_requests_report
-
Hash[
-
self.requests.find(
-
:all,
-
:select=>'request_type_id,count(requests.id) AS total',
-
:group=>'request_type_id'
-
).map {|rt| [rt.request_type_id,rt.total] }
-
]
-
end
-
end
-
-
class Counter
-
def initialize
-
@statistics = Hash.new { |h,k| h[k] = 0 }
-
end
-
-
delegate :[], :[]=, :to => :@statistics
-
-
def total
-
@statistics.values.sum
-
end
-
-
def completed
-
[ 'passed', 'failed' ].map(&method(:[])).sum
-
end
-
-
def pending
-
[ 'pending', 'blocked' ].map(&method(:[])).sum
-
end
-
-
[ :started, :passed, :failed, :cancelled ].each do |direct_type|
-
class_eval(%Q{def #{direct_type} ; self[#{direct_type.to_s.inspect}] ; end})
-
end
-
-
def progress
-
return 0 if self.passed.zero? # If there are no passed then the progress is 0% by definition
-
(self.passed * 100) / (self.total - self.failed)
-
end
-
end
-
-
class Summary
-
def initialize
-
@counters = Hash.new { |h,k| h[k] = Counter.new }
-
end
-
-
delegate :[], :[]=, :to => :@counters
-
-
def self.summary_counter(name)
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{name}
-
@counters.values.map(&#{name.to_sym.inspect}).sum
-
end
-
}, __FILE__, line)
-
end
-
-
[ :started, :passed, :failed, :cancelled, :completed, :pending ].each do |name|
-
summary_counter(name)
-
end
-
end
-
-
# Returns a hash that maps from the RequestType to the information about the number of requests in various
-
# states. This is effectively summary data that can be displayed in a tabular format for the user.
-
1
def progress_statistics
-
counters = self.all(:select => 'request_type_id, state, count(distinct requests.id) as total', :group => 'request_type_id, state', :include=>:request_type)
-
tabulated = Hash.new { |h,k| h[k] = Counter.new }
-
tabulated.tap do
-
counters.each do |request_type_state_count|
-
tabulated[request_type_state_count.request_type][request_type_state_count.state] = request_type_state_count.total.to_i
-
end
-
end
-
end
-
-
1
def asset_statistics(options = {})
-
counters = self.all(options.merge(:select => 'asset_id,request_type_id,state, count(*) as total', :group => 'asset_id, request_type_id, state', :include=>:request_type))
-
tabulated = Hash.new { |h,k| h[k] = Summary.new }
-
tabulated.tap do
-
counters.each do |asset_request_type_state_count|
-
tabulated[asset_request_type_state_count.asset_id.to_i][asset_request_type_state_count.request_type_id.to_i][asset_request_type_state_count.state] = asset_request_type_state_count.total.to_i
-
end
-
end
-
end
-
-
1
def sample_statistics(options = {})
-
counters = self.join_asset.all(options.merge(:select => 'sample_id,request_type_id,state,count(*) as total', :group => 'sample_id, request_type_id, state', :include=>:request_type))
-
tabulated = Hash.new { |h,k| h[k] = Summary.new }
-
tabulated.tap do
-
counters.each do |sample_request_type_state_count|
-
tabulated[sample_request_type_state_count.sample_id.to_i][sample_request_type_state_count.request_type_id.to_i][sample_request_type_state_count.state] = sample_request_type_state_count.total.to_i
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class RequestEvent < ActiveRecord::Base
-
-
1
belongs_to :request
-
-
1
validates :request, :to_state, :current_from, :event_name, :presence => true
-
-
1
validates_inclusion_of :event_name, :in => ['created','state_changed','destroyed']
-
-
1
scope :current, -> { where( :current_to => nil ) }
-
-
1
def expire!(date_time)
-
raise StandardError, 'This event has already expired!' unless current_to.nil?
-
self.update_attributes!(:current_to=>date_time)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2015 Genome Research Ltd.
-
1
class RequestFactory
-
1
def self.copy_request(request)
-
ActiveRecord::Base.transaction do
-
-
request.class.create!(request.attributes) do |request_copy|
-
request_copy.target_asset_id = nil
-
request_copy.state = "pending"
-
request_copy.request_metadata_attributes = request.request_metadata.attributes
-
request_copy.created_at = Time.now
-
end
-
end
-
end
-
-
1
def self.create_assets_requests(assets, study)
-
# internal requests to link study -> request -> asset -> sample
-
# TODO: do this as a submission
-
request_type = RequestType.find_by_key!('create_asset')
-
assets.each { |asset| request_type.create!(:study => study, :asset => asset, :state => 'passed') }
-
end
-
-
1
def self.create_external_multiplexed_library_creation_requests(sources,target,study)
-
request_type = RequestType.find_by_key!('external_multiplexed_library_creation')
-
sources.each { |asset| request_type.create!(:study => study, :asset => asset, :target_asset => target ) }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class RequestInformation < ActiveRecord::Base
-
1
belongs_to :request_information_type
-
1
belongs_to :request
-
-
1
scope :information_type, ->(*args) { {:conditions => { :request_information_type_id => args[0]} } }
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class RequestInformationType < ActiveRecord::Base
-
1
has_many :pipeline_request_information_types
-
1
has_many :pipelines, :through => :pipeline_request_information_types
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
# A RequestPurpose is a simple means of distinguishing WHY a request was made.
-
# cf. RequestType which defines how it will be fulfilled.
-
# Both RequestType and Request have a purpose, with the former acting as the default for
-
# the latter.
-
1
class RequestPurpose < ActiveRecord::Base
-
-
1
STANDARD_PURPOSE = 'standard'
-
-
1
validates_presence_of :key
-
1
validates_uniqueness_of :key
-
-
1
has_many :requests
-
1
has_many :request_types
-
-
1
def self.standard
-
1
self.find_by_key(STANDARD_PURPOSE)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
class RequestType < ActiveRecord::Base
-
-
include RequestType::Validation
-
-
class DeprecatedError < RuntimeError; end
-
-
class RequestTypePlatePurpose < ActiveRecord::Base
-
self.table_name =('request_type_plate_purposes')
-
-
belongs_to :request_type
-
validates_presence_of :request_type
-
belongs_to :plate_purpose
-
validates_presence_of :plate_purpose
-
validates_uniqueness_of :plate_purpose_id, :scope => :request_type_id
-
end
-
-
include Workflowed
-
include Uuid::Uuidable
-
include SharedBehaviour::Named
-
-
has_many :requests, :inverse_of => :request_type
-
has_many :pipelines_request_types, :inverse_of => :request_type
-
has_many :pipelines, :through => :pipelines_request_types
-
has_many :library_types_request_types, :inverse_of=> :request_type
-
has_many :library_types, :through => :library_types_request_types
-
has_many :request_type_validators, :class_name => 'RequestType::Validator'
-
-
belongs_to :pooling_method, :class_name => 'RequestType::PoolingMethod'
-
has_many :request_type_extended_validators, :class_name => 'ExtendedValidator::RequestTypeExtendedValidator'
-
has_many :extended_validators, :through => :request_type_extended_validators, :dependent => :destroy
-
-
def default_library_type
-
library_types.find(:first,:conditions=>{:library_types_request_types=>{:is_default=>true}})
-
end
-
-
# Returns a collect of pipelines for which this RequestType is valid control.
-
# ...so only valid for ControlRequest producing RequestTypes...
-
has_many :control_pipelines, :class_name => 'Pipeline', :foreign_key => :control_request_type_id
-
belongs_to :product_line
-
-
# Couple of named scopes for finding billable types
-
scope :billable, -> { where( :billable => true ) }
-
scope :non_billable, -> { where( :billable => false ) }
-
-
# Defines the acceptable plate purposes or the request type. Essentially this is used to limit the
-
# cherrypick plate types when going into pulldown to the correct list.
-
has_many :plate_purposes, :class_name => 'RequestType::RequestTypePlatePurpose'
-
has_many :acceptable_plate_purposes, :through => :plate_purposes, :source => :plate_purpose
-
-
# While a request type describes what a request is, a request purpose describes why it is being done.
-
# ie. standrad, qc, internal
-
# The value on request type acts as a default for requests
-
belongs_to :request_purpose
-
validates_presence_of :request_purpose
-
-
MORPHOLOGIES = [
-
LINEAR = 0, # one-to-one
-
CONVERGENT = 1, # many-to-one
-
DIVERGENT = 2 # one-to-many
-
# we don't do many-to-many so far
-
]
-
-
validates_presence_of :order
-
validates_numericality_of :order, :integer_only => true
-
validates_numericality_of :morphology, :in => MORPHOLOGIES
-
validates_presence_of :request_class_name
-
-
serialize :request_parameters
-
-
delegate :accessioning_required?, :to => :request_class
-
-
scope :applicable_for_asset, ->(asset) {
-
where([
-
'asset_type = ?
-
AND request_class_name != "ControlRequest"
-
AND deprecated IS FALSE',
-
asset.asset_type_for_request_types.name
-
])
-
}
-
-
# Helper method for generating a request constructor, like 'create!'
-
def self.request_constructor(name, options = {})
-
target = options[:target] || :request_class
-
target_method = options[:method] || name
-
-
line = __LINE__ + 1
-
class_eval(%Q{
-
def #{name}(attributes = nil, &block)
-
raise RequestType::DeprecatedError if self.deprecated
-
attributes ||= {}
-
#{target}.#{target_method}(attributes.merge(request_parameters || {})) do |request|
-
request.request_type = self
-
request.request_purpose ||= self.request_purpose
-
yield(request) if block_given?
-
end.tap do |request|
-
requests << request
-
end
-
end
-
}, __FILE__, line)
-
end
-
-
request_constructor(:create!)
-
request_constructor(:new)
-
alias_method(:new_request, :new)
-
-
request_constructor(:create_control!, :target => 'ControlRequest', :method => :create!)
-
-
def request_class
-
request_class_name.constantize
-
end
-
-
def request_class=(request_class)
-
self.request_class_name = request_class.name
-
end
-
-
def self.dna_qc
-
find_by_key("dna_qc") or raise "Cannot find dna_qc request type"
-
end
-
-
def self.genotyping
-
find_by_key("genotyping") or raise "Cannot find genotyping request type"
-
end
-
-
def self.transfer
-
find_by_key("transfer") or raise "Cannot find transfer request type"
-
end
-
-
def extract_metadata_from_hash(request_options)
-
# WARNING: we need a copy of the options (we delete stuff from attributes)
-
return {} unless request_options
-
attributes = request_options.symbolize_keys
-
common_attributes = request_class::Metadata.attribute_details.map(&:name)
-
common_attributes.concat(request_class::Metadata.association_details.map(&:assignable_attribute_name))
-
attributes.delete_if { |k,_| not common_attributes.include?(k) }
-
end
-
-
def targets_lanes?
-
(target_asset_type == 'Lane') or (name =~ /\ssequencing$/)
-
end
-
-
# The target asset can either be described by a purpose, or by the target asset type.
-
belongs_to :target_purpose, :class_name => 'Purpose'
-
-
def needs_target_asset?
-
target_purpose.nil? and target_asset_type.blank?
-
end
-
-
def create_target_asset!(&block)
-
case
-
when target_purpose.present? then target_purpose.create!(&block)
-
when target_asset_type.blank? then nil
-
else target_asset_type.constantize.create!(&block)
-
end
-
end
-
-
delegate :pool_count, :to => :pooling_method
-
1
delegate :pool_index_for_asset, :to => :pooling_method
-
delegate :pool_index_for_request, :to => :pooling_method
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class RequestType::PoolingMethod < ActiveRecord::Base
-
-
1
has_many :request_types
-
1
validates_presence_of :pooling_behaviour
-
1
serialize :pooling_options
-
-
1
self.table_name=('pooling_methods')
-
-
1
after_initialize :import_behaviour
-
-
1
def import_behaviour
-
return if pooling_behaviour.nil?
-
behavior_module = "RequestType::PoolingMethod::#{pooling_behaviour}".constantize
-
self.class_eval do
-
include(behavior_module)
-
end
-
end
-
-
1
def pooling_behaviour=(*params)
-
super
-
import_behaviour
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
##
-
# Set on a multiplexed request_type
-
# Pools based on the row of the source asset.
-
# WARNING: If target assets are to be created upfront, source assets must be defined
-
# WARNING: Will pool based on source asset location, not target. So may show odd behaviour
-
# with re-arrays.
-
1
module RequestType::PoolingMethod::PlateRow
-
-
1
def pool_count
-
pooling_options[:pool_count]
-
end
-
-
1
def pool_index_for_asset(source_asset)
-
# This isn't ideal. We can't get the pool index until we have a source asset.
-
return 0 unless source_asset.present?
-
source_asset.map.row
-
end
-
-
1
def pool_index_for_request(request)
-
return pool_index_for_asset(request.asset) if request.asset.present?
-
# If we don't have an asset yet, look upstream. This assumes no
-
# re-arraying has taken place.
-
raise StandardError, "Finding the pool index before requests are attached is unsupported"
-
end
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
##
-
# Set on a multiplexed request_type
-
# Pools based on the submission.
-
1
module RequestType::PoolingMethod::Submission
-
1
def pool_count
-
1
-
end
-
-
1
def pool_index_for_asset(_)
-
0
-
end
-
-
1
def pool_index_for_request(_)
-
0
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
module RequestType::Validation
-
-
1
def delegate_validator
-
DelegateValidation::CompositeValidator::CompositeValidator(request_class.delegate_validator,request_type_validator)
-
end
-
-
1
def request_type_validator
-
request_type = self
-
-
Class.new(RequestTypeValidator) do
-
request_type.request_type_validators.each do |validator|
-
message = "is '%{value}' should be #{validator.valid_options.to_sentence(:last_word_connector=>' or ')}"
-
vro = :"#{validator.request_option}"
-
delegate_attribute(vro, :to => :target, :default => validator.default, :type_cast => validator.type_cast)
-
validates_inclusion_of vro, :in => validator.valid_options, :if => :"#{validator.request_option}_needs_checking?", :message => message
-
end
-
end.tap do |sub_class|
-
sub_class.request_type = request_type
-
end
-
-
end
-
-
1
class RequestTypeValidator < DelegateValidation::Validator
-
1
class_attribute :request_type, :instance_writer => false
-
1
request_type = nil
-
-
1
def library_types_present?
-
request_type.library_types.present?
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
##
-
# A request type validator belongs to a request type, and is responsible for
-
# validating a single request option
-
# request_option => The option that will be validated
-
# valid_options => A serialized object that responds to include? Returning true if the option is present
-
# It should also return an array of valid options in response to to_a
-
class RequestType::Validator < ActiveRecord::Base
-
-
class LibraryTypeValidator
-
attr_reader :request_type_id
-
def initialize(request_type_id)
-
@request_type_id = request_type_id
-
end
-
def request_type
-
RequestType.find(request_type_id)
-
end
-
def include?(option)
-
request_type.library_types.map(&:name).include?(option)
-
end
-
def default
-
request_type.default_library_type.try(:name)
-
end
-
def to_a
-
request_type.library_types.map(&:name)
-
1
end
-
delegate :to_sentence, :to => :to_a
-
end
-
-
##
-
# Array class that lets you set a default value
-
# If first argument is an array, second argument is assumed to be default
-
# Raises exception is default is not in the array
-
# In all other cases passes argument to standard array initializer
-
1
class ArrayWithDefault
-
1
attr_accessor :default
-
-
1
def initialize(array,default)
-
raise StandardError, "Default is not in array" unless array.include?(default)
-
@default = default
-
@array = array
-
end
-
-
1
def method_missing(method,*args,&block)
-
@array.send(method,*args,&block)
-
end
-
-
1
def include?(option)
-
# We have to define include specifically
-
@array.include?(option)
-
end
-
-
1
def to_a
-
@array
-
end
-
end
-
-
1
belongs_to :request_type
-
1
validates :request_type, :request_option, :valid_options, :presence => true
-
1
serialize :valid_options
-
-
1
def include?(option)
-
valid_options.include?(option)
-
end
-
-
1
def options
-
valid_options.to_a
-
end
-
-
1
def default
-
valid_options.respond_to?(:default) ? valid_options.default : nil
-
end
-
-
1
def type_cast
-
{
-
'read_length' => :to_i,
-
'insert_size' => :to_i
-
}[request_option]
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class ResourceProxy
-
1
def initialize(resource, resource_class)
-
@resource_id = case resource
-
when String
-
resource
-
when Integer
-
resource
-
when Hash
-
resource["id"] #|| resource[:id]
-
when NilClass
-
nil
-
else
-
@object = resource
-
resource.id
-
end
-
@resource_class = resource_class
-
end
-
-
1
def id
-
@resource_id
-
end
-
-
1
def object
-
@object ||= @resource_class.find(@resource_id)
-
end
-
-
1
def set_object(object)
-
@object = object
-
end
-
-
1
def loaded?
-
@object ? true : false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.
-
1
class Robot < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include ModelExtensions::Robot
-
1
validates_presence_of :name
-
1
validates_presence_of :location
-
1
has_many :robot_properties
-
1
has_one :max_plates_property, :class_name => 'RobotProperty', :conditions => { :key => 'max_plates' }
-
-
1
scope :with_machine_barcode, ->(barcode) {
-
barcode_number = Barcode.number_to_human(barcode)
-
{ :conditions => [ 'barcode=? AND ?', barcode_number, Barcode.prefix_from_barcode(barcode)==prefix ] }
-
}
-
-
1
scope :include_properties, -> { includes(:robot_properties) }
-
-
1
def minimum_volume
-
configatron.tecan_minimum_volume
-
end
-
-
1
def max_beds
-
max_plates_property.try(:value).to_i
-
end
-
-
1
def self.prefix
-
"RB"
-
end
-
-
1
def self.find_from_barcode(code)
-
human_robot_barcode = Barcode.number_to_human(code)
-
Robot.find_by_barcode(human_robot_barcode) || Robot.find_by_id(human_robot_barcode)
-
end
-
-
1
def self.valid_barcode?(code)
-
Barcode.barcode_to_human!(code, self.prefix)
-
self.find_from_barcode(code) # an exception is raise if not found
-
true
-
rescue
-
false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
class RobotProperty < ActiveRecord::Base
-
1
belongs_to :robot
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
class RobotVerification
-
-
1
attr_reader :errors
-
-
1
def validate_barcode_params(barcode_hash)
-
return yield("No barcodes specified") if barcode_hash.nil?
-
yield("Worksheet barcode invalid") if barcode_hash[:batch_barcode].nil? or not Batch.valid_barcode?(barcode_hash[:batch_barcode])
-
yield("Tecan robot barcode invalid") if barcode_hash[:robot_barcode].nil? or not Robot.valid_barcode?(barcode_hash[:robot_barcode])
-
yield("User barcode invalid") if barcode_hash[:user_barcode].nil? or not User.valid_barcode?(barcode_hash[:user_barcode])
-
yield("Destination plate barcode invalid") if barcode_hash[:destination_plate_barcode].nil? or Plate.find_by_barcode(Barcode.number_to_human(barcode_hash[:destination_plate_barcode])).nil?
-
end
-
-
1
def expected_layout(batch, destination_plate_barcode)
-
plate_barcode = Barcode.number_to_human(destination_plate_barcode) || Plate.find_from_machine_barcode(destination_plate_barcode).barcode
-
batch.tecan_layout_plate_barcodes(plate_barcode)
-
end
-
-
1
def valid_source_plates_on_robot?(beds, plates, robot, batch,all_expected_plate_layout)
-
valid_plates_on_robot?(beds, plates, "SCRC", robot, batch, all_expected_plate_layout[1])
-
end
-
-
1
def valid_destination_plates_on_robot?(beds, plates, robot, batch, all_expected_plate_layout)
-
valid_plates_on_robot?(beds, plates, "DEST", robot, batch, all_expected_plate_layout[0])
-
end
-
-
1
def valid_plates_on_robot?(beds, plates, bed_prefix, robot, batch, expected_plate_layout)
-
return false if expected_plate_layout.blank?
-
expected_plate_layout.each do |plate_barcode, bed_number|
-
scanned_bed_barcode = Barcode.number_to_human(beds["#{bed_number}"].strip)
-
expected_bed_barcode = robot.robot_properties.find_by_key!("#{bed_prefix}#{bed_number}")
-
return false if expected_bed_barcode.nil?
-
return false if scanned_bed_barcode != expected_bed_barcode.value
-
return false if plates[plate_barcode].strip != plate_barcode
-
end
-
-
true
-
end
-
-
1
def valid_plate_locations?(params, batch, robot, expected_plate_layout)
-
return false if ! valid_source_plates_on_robot?(params[:bed_barcodes],params[:plate_barcodes], robot,batch,expected_plate_layout)
-
return false if ! valid_destination_plates_on_robot?(params[:destination_bed_barcodes],params[:destination_plate_barcodes], robot,batch,expected_plate_layout)
-
-
true
-
end
-
-
1
def valid_submission?(params)
-
destination_plate_barcode = params[:barcodes][:destination_plate_barcode]
-
batch = Batch.find_by_id(params[:batch_id])
-
robot = Robot.find_by_id(params[:robot_id])
-
user = User.find_by_id(params[:user_id])
-
-
@errors = []
-
@errors << "Could not find batch #{params[:batch_id]}" if batch.nil?
-
@eerors << "Could not find robot" if robot.nil?
-
@errors << "Could not find user" if user.nil?
-
@errors << "No destination barcode specified" if destination_plate_barcode.blank?
-
return false unless @errors.empty?
-
-
expected_plate_layout = self.expected_layout(batch, destination_plate_barcode)
-
-
if valid_plate_locations?(params, batch, robot, expected_plate_layout)
-
batch.events.create(
-
:message => I18n.t("bed_verification.layout.valid", :plate_barcode => destination_plate_barcode),
-
:created_by => user.login)
-
else
-
batch.events.create(
-
:message => I18n.t("bed_verification.layout.invalid", :plate_barcode => destination_plate_barcode),
-
:created_by => user.login)
-
@errors << "Bed layout invalid"
-
return false
-
end
-
-
true
-
end
-
-
1
def set_plate_types(plate_types_params)
-
plate_types_params.each do |plate_barcode, plate_type|
-
next if plate_barcode.blank? || plate_type.blank?
-
plate = Plate.with_machine_barcode(plate_barcode).first or raise "Unable to locate plate #{plate_barcode.inspect} for robot verification"
-
plate.set_plate_type(plate_type)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
# Defines named roles for users that may be applied to
-
# objects in a polymorphic fashion. For example, you could create a role
-
# "moderator" for an instance of a model (i.e., an object), a model class,
-
# or without any specification at all.
-
1
class Role < ActiveRecord::Base
-
1
class UserRole < ActiveRecord::Base
-
1
self.table_name =('roles_users')
-
1
belongs_to :role
-
1
belongs_to :user
-
end
-
-
1
has_many :user_role_bindings, :class_name => 'Role::UserRole'
-
1
has_many :users, :through => :user_role_bindings, :source => :user
-
-
1
belongs_to :authorizable, :polymorphic => true
-
-
1
validates_presence_of :name
-
1
scope :general_roles, -> { where("authorizable_type IS NULL") }
-
-
1
def self.keys
-
Role.all.map { |r| r.name }.uniq
-
end
-
-
1
def before_destroy
-
authorizable.touch unless authorizable.nil?
-
end
-
-
# Include this module into your ActiveRecord model and get has_many roles and some
-
# utility named_scopes. You also get the ability to define role relations by name
-
# through the role_relation class method.
-
1
module Authorized
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
1
base.instance_eval do
-
1
has_many :roles, :as => :authorizable
-
-
1
scope :with_related_users_included, -> { includes(:roles => :users ) }
-
1
scope :of_interest_to, ->(user) {
-
{
-
:joins => joins_through_to_users,
-
:conditions => ['rj_u.id=?', user.id],
-
:group => "rj_r.authorizable_id"
-
}
-
}
-
end
-
end
-
-
1
module ClassMethods
-
1
def joins_through_to_users
-
[
-
"INNER JOIN roles rj_r ON rj_r.authorizable_type IN (#{[self,*self.descendants].map{|c|"'#{c.name}'"}.join(',')}) AND rj_r.authorizable_id=#{table_name}.id",
-
"INNER JOIN roles_users rj_ru ON rj_r.id=rj_ru.role_id",
-
"INNER JOIN users rj_u ON rj_u.id=rj_ru.user_id"
-
]
-
end
-
1
private :joins_through_to_users
-
-
1
def role_relation(name, role_name)
-
3
scope name.to_sym, ->(user) {
-
joins(joins_through_to_users).
-
where(['rj_r.name=? AND rj_u.id=?', role_name.to_s, user.id ])
-
}
-
end
-
-
1
def has_many_users_through_roles(name)
-
4
define_method(name.to_s.pluralize.to_sym) do
-
role = self.roles.find_by_name(name.to_s.singularize)
-
role.nil? ? [] : role.users
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
require 'rexml/text'
-
class Sample < ActiveRecord::Base
-
include ModelExtensions::Sample
-
include Api::SampleIO::Extensions
-
-
-
self.per_page = 500
-
include ExternalProperties
-
include Identifiable
-
include Uuid::Uuidable
-
include StandardNamedScopes
-
include SharedBehaviour::Named
-
include Aliquot::Aliquotable
-
-
extend EventfulRecord
-
has_many_events do
-
event_constructor(:created_using_sample_manifest!, Event::SampleManifestEvent, :created_sample!)
-
event_constructor(:updated_using_sample_manifest!, Event::SampleManifestEvent, :updated_sample!)
-
end
-
-
has_many_lab_events
-
-
ArrayExpressFields = %w(genotype phenotype strain_or_line developmental_stage sex cell_type disease_state compound dose immunoprecipitate growth_condition rnai organism_part species time_point age treatment)
-
EgaFields = %w(subject disease treatment gender phenotype)
-
-
acts_as_authorizable
-
-
has_many :study_samples, :dependent => :destroy
-
has_many :studies, :through => :study_samples
-
-
has_many :roles, :as => :authorizable
-
has_many :comments, :as => :commentable
-
-
receptacle_alias(:assets) do
-
def first_of_type(asset_class)
-
self.detect { |asset| asset.is_a?(asset_class) }
-
end
-
end
-
receptacle_alias(:wells, :class_name => 'Well')
-
receptacle_alias(:sample_tubes, :class_name => 'SampleTube')
-
-
has_many :asset_groups, :through => :assets
-
has_many :requests, :through => :assets
-
has_many :submissions, :through => :requests
-
-
belongs_to :sample_manifest
-
-
validates_presence_of :name
-
validates_format_of :name, :with => /^[\w_-]+$/i, :message => I18n.t('samples.name_format'), :if => :new_name_format, :on => :create
-
validates_format_of :name, :with => /^[\(\)\+\s\w._-]+$/i, :message => I18n.t('samples.name_format'), :if => :new_name_format, :on => :update
-
validates_uniqueness_of :name, :on => :create, :message => "already in use", :unless => :sample_manifest_id?
-
-
validate :name_unchanged, :if => :name_changed?, :on => :update
-
-
def name_unchanged
-
errors.add(:name, 'cannot be changed') unless can_rename_sample
-
can_rename_sample
-
end
-
-
extend ValidationStateGuard
-
validation_guard(:can_rename_sample)
-
-
def rename_to!(new_name)
-
update_attributes!(:name => new_name)
-
end
-
validation_guarded_by(:rename_to!, :can_rename_sample)
-
-
before_destroy :safe_to_destroy
-
-
def safe_to_destroy
-
return true unless receptacles.present? || has_submission?
-
errors.add(:base,"Remove '#{name}' from assets before destroying") if receptacles.present?
-
errors.add(:base,"You can't delete '#{name}' because is linked to a submission.") if has_submission?
-
return false
-
end
-
private :safe_to_destroy
-
-
scope :with_name, ->(*names) { where(:name => names.flatten) }
-
-
scope :for_search_query, ->(query,with_includes) {
-
where(['name LIKE ? OR id=?', "%#{query}%", query ])
-
}
-
-
scope :non_genotyped, -> { where("samples.id not in (select propertied_id from external_properties where propertied_type = 'Sample' and `key` = 'genotyping_done' )") }
-
-
scope :on_plate, ->(plate) {
-
joins([
-
'INNER JOIN aliquots ON aliquots.sample_id = samples.id',
-
'INNER JOIN container_associations AS ca ON ca.content_id = aliquots.receptacle_id'
-
]).
-
where(['ca.container_id = ?',plate.id])
-
}
-
-
scope :for_plate_and_order, ->(plate_id,order_id) {
-
joins([
-
'INNER JOIN aliquots ON aliquots.sample_id = samples.id',
-
'INNER JOIN container_associations AS ca ON ca.content_id = aliquots.receptacle_id',
-
'INNER JOIN well_links ON target_well_id = aliquots.receptacle_id AND well_links.type = "stock"',
-
'INNER JOIN requests ON requests.asset_id = well_links.source_well_id'
-
]).
-
where(['ca.container_id = ? AND requests.order_id = ?',plate_id,order_id])
-
}
-
-
scope :for_plate_and_order_as_target, ->(plate_id,order_id) {
-
joins([
-
'INNER JOIN aliquots ON aliquots.sample_id = samples.id',
-
'INNER JOIN container_associations AS ca ON ca.content_id = aliquots.receptacle_id',
-
'INNER JOIN requests ON requests.target_asset_id = aliquots.receptacle_id'
-
]).
-
where(['ca.container_id = ? AND requests.order_id = ?',plate_id,order_id])
-
}
-
-
def self.by_name(sample_id)
-
self.find_by_name(sample_id)
-
end
-
-
def select_study(sample_id)
-
sample = self.find(sample_id)
-
sample.studies
-
end
-
-
def shorten_sanger_sample_id
-
short_sanger_id = case sanger_sample_id
-
when blank? then name
-
when sanger_sample_id.size <10 then sanger_sample_id
-
when /([\d]{7})$/ then $1
-
else
-
sanger_sample_id
-
end
-
-
short_sanger_id
-
end
-
-
def has_request
-
requests.present?
-
end
-
-
def has_request_all_cancelled?
-
self.requests.all?(&:cancelled?)
-
end
-
-
def has_submission?
-
has_submission = false
-
if self.has_request
-
if has_request_all_cancelled?
-
sra_hold_value = self.sample_metadata.sample_sra_hold
-
if sra_hold_value.nil?
-
has_submission = false
-
elsif 'hold' != sra_hold_value
-
has_submission = true
-
else
-
has_submission = false
-
end
-
else
-
has_submission = true
-
end
-
else # We have no requests, we're probably S2 (Or very old Sequencescape)
-
# This is a hack, but I'll get this tidied up.
-
has_submission = true
-
end
-
return has_submission
-
end
-
-
def has_ebi_accession_number
-
has_ebi_accession_number = false
-
-
self.studies.each do |study|
-
if ! study.ebi_accession_number.blank?
-
has_ebi_accession_number = true
-
end
-
end
-
-
return has_ebi_accession_number
-
end
-
-
# TODO: remove as this is no longer needed (validation of name change will fail)
-
# On update, checks if updating the name is possible
-
def name_change?(new_name)
-
self.name == new_name ? false : true
-
end
-
-
# TODO: move to sample_metadata and delegate
-
def released?
-
self.sample_metadata.sample_sra_hold == 'Public'
-
end
-
-
def release
-
self.sample_metadata.sample_sra_hold = 'Public'
-
self.sample_metadata.save!
-
end
-
-
def ebi_accession_number
-
self.sample_metadata.sample_ebi_accession_number
-
end
-
-
def accession_number?
-
not self.ebi_accession_number.blank?
-
end
-
-
# If there is no existing ebi_accession_number and we have a taxon id
-
# and we have a common name for the sample return true else false
-
def accession_could_be_generated?
-
return false unless self.sample_metadata.sample_ebi_accession_number.blank?
-
required_tags.each do |tag|
-
return false if self.sample_metadata.send(tag).blank?
-
end
-
# We have everything needed to generate an accession so...
-
true
-
end
-
-
def self.submissions_by_assets(study_id, asset_group_id)
-
return [] if asset_group_id == '0'
-
-
study = Study.find(study_id)
-
asset_group_assets = AssetGroupAsset.find(:all, :conditions => [" asset_group_id = ? ", asset_group_id])
-
return study.submissions.that_submitted_asset_id(asset_group_assets.first.asset_id).all
-
end
-
-
def error
-
"Default error message"
-
end
-
-
def sample_external_name
-
self.name
-
end
-
-
def sample_empty?(supplier_sample_name = self.name)
-
return true if self.empty_supplier_sample_name
-
sample_supplier_name_empty?(supplier_sample_name)
-
end
-
-
def sample_supplier_name_empty?(supplier_sample_name)
-
supplier_sample_name.blank? || [ 'empty', 'blank', 'water', 'no supplier name available', 'none' ].include?(supplier_sample_name.downcase)
-
end
-
-
def accession_service
-
return nil if self.studies.empty?
-
self.studies.first.accession_service
-
end
-
-
# at the moment return a string which is a comma separated list of snp plate id
-
def genotyping_done
-
self.get_external_value('genotyping_done')
-
end
-
-
def genotyping_snp_plate_id
-
s = genotyping_done
-
if s && s =~ /:/
-
s.split(":").second.to_i # take the firt integer
-
else # old value
-
""
-
end
-
end
-
-
GC_CONTENTS = [ 'Neutral', 'High AT', 'High GC' ]
-
GENDERS = [ 'Male', 'Female', 'Mixed', 'Hermaphrodite', 'Unknown', 'Not Applicable' ]
-
DNA_SOURCES = [ 'Genomic', 'Whole Genome Amplified', 'Blood', 'Cell Line','Saliva','Brain','FFPE',
-
'Amniocentesis Uncultured', 'Amniocentesis Cultured', 'CVS Uncultured', 'CVS Cultured', 'Fetal Blood', 'Tissue' ]
-
SRA_HOLD_VALUES = [ 'Hold', 'Public', 'Protect' ]
-
AGE_REGEXP = '\d+(?:\.\d+|\-\d+|\.\d+\-\d+\.\d+|\.\d+\-\d+\.\d+)?\s+(?:second|minute|day|week|month|year)s?|Not Applicable|N/A|To be provided'
-
DOSE_REGEXP = '\d+(?:\.\d+)?\s+\w+(?:\/\w+)?|Not Applicable|N/A|To be provided'
-
-
extend Metadata
-
has_metadata do
-
include ReferenceGenome::Associations
-
association(:reference_genome, :name, :required => true)
-
-
attribute(:organism)
-
attribute(:cohort)
-
attribute(:country_of_origin)
-
attribute(:geographical_region)
-
attribute(:ethnicity)
-
attribute(:volume)
-
attribute(:supplier_plate_id)
-
attribute(:mother)
-
attribute(:father)
-
attribute(:replicate)
-
attribute(:gc_content, :in => Sample::GC_CONTENTS)
-
attribute(:gender, :in => Sample::GENDERS)
-
attribute(:donor_id)
-
attribute(:dna_source, :in => Sample::DNA_SOURCES)
-
attribute(:sample_public_name)
-
attribute(:sample_common_name)
-
attribute(:sample_strain_att)
-
attribute(:sample_taxon_id)
-
attribute(:sample_ebi_accession_number)
-
attribute(:sample_description)
-
attribute(:sample_sra_hold, :in => Sample::SRA_HOLD_VALUES)
-
-
attribute(:sibling)
-
attribute(:is_resubmitted) # TODO[xxx]: selection of yes/no?
-
attribute(:date_of_sample_collection) # TODO[xxx]: Date field?
-
attribute(:date_of_sample_extraction) # TODO[xxx]: Date field?
-
attribute(:sample_extraction_method)
-
attribute(:sample_purified) # TODO[xxx]: selection of yes/no?
-
attribute(:purification_method) # TODO[xxx]: tied to the field above?
-
attribute(:concentration)
-
attribute(:concentration_determined_by)
-
attribute(:sample_type)
-
attribute(:sample_storage_conditions)
-
-
# Array Express
-
attribute(:genotype)
-
attribute(:phenotype)
-
#attribute(:strain_or_line) strain
-
#TODO: split age in two fields and use a composed_of
-
attribute(:age, :with => Regexp.new("^#{Sample::AGE_REGEXP}$"))
-
attribute(:developmental_stage)
-
#attribute(:sex) gender
-
attribute(:cell_type)
-
attribute(:disease_state)
-
attribute(:compound) #TODO : yes/no?
-
attribute(:dose, :with => Regexp.new("^#{Sample::DOSE_REGEXP}$"))
-
attribute(:immunoprecipitate)
-
attribute(:growth_condition)
-
attribute(:rnai)
-
attribute(:organism_part)
-
#attribute(:species) common name
-
attribute(:time_point)
-
-
#EGA
-
attribute(:treatment)
-
attribute(:subject)
-
attribute(:disease)
-
-
-
with_options(:if => :validating_ena_required_fields?) do |ena_required_fields|
-
# ena_required_fields.validates_presence_of :sample_common_name
-
# ena_required_fields.validates_presence_of :sample_taxon_id
-
ena_required_fields.validates_presence_of :service_specific_fields
-
end
-
-
# The spreadsheets that people upload contain various fields that could be mistyped. Here we ensure that the
-
# capitalisation of these is correct.
-
REMAPPED_ATTRIBUTES = {
-
:gc_content => GC_CONTENTS,
-
:gender => GENDERS,
-
:dna_source => DNA_SOURCES,
-
:sample_sra_hold => SRA_HOLD_VALUES
-
# :reference_genome => ??
-
}.inject({}) do |h,(k,v)|
-
h[k] = v.inject({}) { |a,b| a[b.downcase] = b ; a }
-
h
-
end
-
-
before_validation do |record|
-
10
record.reference_genome_id = 1 if record.reference_genome_id.blank?
-
-
# Unfortunately it appears that some of the functionality of this implementation relies on non-capitalisation!
-
# So we remap the lowercased versions to their proper values here
-
10
REMAPPED_ATTRIBUTES.each do |attribute,mapping|
-
40
record[attribute] = mapping.fetch(record[attribute].try(:downcase), record[attribute])
-
40
record[attribute] = nil if record[attribute].blank? # Empty strings should be nil
-
end
-
end
-
end
-
-
include_tag(:sample_strain_att)
-
include_tag(:sample_description)
-
-
include_tag(:gender, :services=>:EGA, :downcase => true)
-
include_tag(:phenotype, :services=>:EGA)
-
include_tag(:donor_id, :services=>:EGA, :as => 'subject_id')
-
-
require_tag(:sample_taxon_id)
-
require_tag(:sample_common_name)
-
require_tag(:gender, :EGA)
-
require_tag(:phenotype, :EGA)
-
require_tag(:donor_id, :EGA)
-
-
# This needs to appear after the metadata has been defined to ensure that the Metadata class
-
# is present.
-
include SampleManifest::InputBehaviour::SampleUpdating
-
-
class Metadata
-
-
attr_reader :reference_genome_set_by_name
-
# here we are aliasing ArrayExpress attribute from normal one
-
# This is easier that way so the name is exactly the name of the array-express field
-
# and the values can be easily remapped
-
# The other solution would be to have a different label for the accession file and the xml/edit page
-
def strain_or_line
-
sample_strain_att
-
end
-
def sex
-
gender && gender.downcase
-
end
-
def species
-
sample_common_name
-
end
-
-
def reference_genome_name=(reference_genome_name)
-
return unless reference_genome_name.present?
-
@reference_genome_set_by_name = reference_genome_name
-
self.reference_genome = ReferenceGenome.find_by_name(reference_genome_name)
-
end
-
-
# If we set a reference genome via its name, we want to validate that we found it.
-
# We can't just raise and exception when we don't find it, as this cases the sample manifest
-
# delayed job to fail completely.
-
validate :reference_genome_found, :if => :reference_genome_set_by_name
-
-
def reference_genome_found
-
# A reference genome of nil automatically get converted to the reference genome named "", so
-
# we need to explicitly check the name has been set as expected.
-
return true if reference_genome.name == reference_genome_set_by_name
-
errors.add(:base,"Couldn't find a Reference Genome with named '#{reference_genome_set_by_name}'.")
-
false
-
end
-
-
end
-
-
-
# Together these two validations ensure that the first study exists and is valid for the ENA submission.
-
validates_each(:ena_study, :if => :validating_ena_required_fields?) do |record, attr, value|
-
record.errors.add(:base,'Sample has no study') if value.blank?
-
end
-
validates_associated(:ena_study, :allow_blank => true, :if => :validating_ena_required_fields?)
-
-
def ena_study
-
@ena_study
-
end
-
-
def validating_ena_required_fields_with_first_study=(state)
-
self.validating_ena_required_fields_without_first_study = state
-
@ena_study.try(:validating_ena_required_fields=, state)
-
end
-
alias_method_chain(:validating_ena_required_fields=, :first_study)
-
-
def validate_ena_required_fields!
-
# Do not alter the order of this line, otherwise @ena_study won't be set correctly!
-
@ena_study, self.validating_ena_required_fields = self.studies.first, true
-
self.valid? or raise ActiveRecord::RecordInvalid, self
-
rescue ActiveRecord::RecordInvalid => exception
-
@ena_study.errors.full_messages.each do |message|
-
self.errors.add(:base,"#{ message } on study")
-
end unless @ena_study.nil?
-
raise exception
-
ensure
-
# Do not alter the order of this line, otherwise the @ena_study won't be reset!
-
self.validating_ena_required_fields, @ena_study = false, nil
-
end
-
-
def sample_reference_genome
-
reference_genome = self.sample_metadata.reference_genome
-
reference_genome = self.primary_study.try(:study_metadata).try(:reference_genome) if ( reference_genome.nil? ) || reference_genome.name.blank?
-
reference_genome
-
end
-
-
def affiliated_with?(object)
-
case
-
when object.respond_to?(:sample_id)
-
self.id == object.sample_id
-
when object.respond_to?(:sample_ids)
-
object.sample_ids.include?(self.id)
-
else
-
nil
-
end
-
end
-
-
def withdraw_consent
-
self.update_attribute(:consent_withdrawn, true)
-
end
-
-
def subject_type
-
'sample'
-
end
-
-
def friendly_name
-
sanger_sample_id||name
-
end
-
-
# These don't really belong here, but exist due to the close coupling between sample
-
# and its initial aliquot in the sample manifest.
-
1
delegate :specialized_from_manifest=, :to => :primary_receptacle
-
delegate :library_information=, :to => :primary_receptacle
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class SampleManifest < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include ModelExtensions::SampleManifest
-
1
include SampleManifest::BarcodePrinterBehaviour
-
1
include SampleManifest::TemplateBehaviour
-
1
include SampleManifest::SampleTubeBehaviour
-
1
include SampleManifest::MultiplexedLibraryBehaviour
-
1
include SampleManifest::CoreBehaviour
-
1
include SampleManifest::PlateBehaviour
-
1
include SampleManifest::InputBehaviour
-
1
include SampleManifest::SharedTubeBehaviour
-
1
extend SampleManifest::StateMachine
-
1
extend Document::Associations
-
-
1
module Associations
-
1
def self.included(base)
-
2
base.has_many(:sample_manifests)
-
end
-
end
-
-
1
has_uploaded_document :uploaded, {:differentiator => "uploaded"}
-
1
has_uploaded_document :generated, {:differentiator => "generated"}
-
-
1
class_attribute :spreadsheet_offset
-
1
class_attribute :spreadsheet_header_row
-
1
self.spreadsheet_offset = 9
-
1
self.spreadsheet_header_row = 8
-
-
1
attr_accessor :override
-
1
attr_reader :manifest_errors
-
-
# Needed for the UI to work!
-
1
def barcode_printer ; end
-
1
def template ; end
-
-
1
belongs_to :supplier
-
1
belongs_to :study
-
1
belongs_to :project
-
1
belongs_to :user
-
1
serialize :last_errors
-
1
serialize :barcodes
-
-
1
validates_presence_of :supplier
-
1
validates_presence_of :study
-
1
validates_numericality_of :count, :only_integer => true, :greater_than => 0, :allow_blank => false
-
-
1
before_save :default_asset_type
-
-
1
def only_first_label
-
false
-
end
-
-
1
def default_asset_type
-
1
self.asset_type = "plate" if self.asset_type.blank?
-
end
-
-
1
def name
-
1
"Manifest_#{self.id}"
-
end
-
-
#TODO[xxx] Consider index to make it faster
-
1
scope :pending_manifests,
-
order( 'sample_manifests.id DESC').
-
joins( 'LEFT OUTER JOIN documents ON documentable_type="SampleManifest" AND documentable_id=sample_manifests.id AND documentable_extended="uploaded"').
-
where( 'documents.id IS NULL')
-
-
1
scope :completed_manifests,
-
order( 'sample_manifests.updated_at DESC').
-
joins( 'LEFT OUTER JOIN documents ON documentable_type="SampleManifest" AND documentable_id=sample_manifests.id AND documentable_extended="uploaded"').
-
where( 'documents.id IS NOT NULL')
-
-
-
1
def generate
-
@manifest_errors = []
-
-
ActiveRecord::Base.transaction do
-
self.barcodes = []
-
core_behaviour.generate
-
end
-
return nil
-
end
-
-
1
def print_labels(barcode_printer, options={})
-
return false if barcode_printer.nil?
-
core_behaviour.print_labels do |printables, prefix, *args|
-
unless printables.empty?
-
printables.each { |printable| printable.study = self.study.abbreviation }
-
printables = [printables.first] if options[:only_first_label]==true
-
barcode_printer.print_labels(printables, prefix, *args)
-
end
-
end
-
true
-
rescue SOAP::FaultError => exception
-
false
-
end
-
-
1
def create_sample(sanger_sample_id)
-
Sample.create!(:name => sanger_sample_id, :sanger_sample_id => sanger_sample_id, :sample_manifest => self).tap do |sample|
-
sample.events.created_using_sample_manifest!(self.user)
-
end
-
end
-
-
1
def generate_sanger_ids(count = 1)
-
(1..count).map { |_| SangerSampleId::Factory.instance.next! }
-
end
-
1
private :generate_sanger_ids
-
-
1
def generate_study_samples(study_samples_data)
-
study_sample_fields = [:study_id, :sample_id]
-
study_samples_data.each do |study_sample|
-
StudySample.create!(:study_id => study_sample.first, :sample_id=> study_sample.last)
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2015 Genome Research Ltd.
-
1
module SampleManifest::BarcodePrinterBehaviour
-
1
ASSET_TYPE_TO_PRINTER_TYPE = {
-
'1dtube' => '1D Tube',
-
'plate' => '96 Well Plate'
-
}
-
-
1
def applicable_barcode_printers
-
printer_type = ASSET_TYPE_TO_PRINTER_TYPE[self.asset_type]
-
printers = BarcodePrinterType.find_by_name(printer_type).barcode_printers unless printer_type.nil?
-
printers = BarcodePrinter.all(:order => 'name ASC') if printers.blank?
-
printers
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
module SampleManifest::CoreBehaviour
-
-
# Include in cores which exhibit the default behaviour
-
module NoSpecializedValidation
-
def validate_specialized_fields(*args); end
-
def specialized_fields(*args); {}; end
-
end
-
-
def self.included(base)
-
1
base.class_eval do
-
delegate :details, :validate_sample_container, :validate_specialized_fields, :specialized_fields, :to => :core_behaviour
-
-
1
attr_accessor :rapid_generation
-
1
alias_method(:rapid_generation?, :rapid_generation)
-
-
1
def self.supported_asset_type?(asset_type)
-
asset_type.nil?||['1dtube','plate','multiplexed_library'].include?(asset_type)
-
end
-
end
-
end
-
-
-
def core_behaviour
-
return @core_behaviour if @core_behaviour.present?
-
-
behaviour = case self.asset_type
-
when '1dtube' then 'SampleTubeBehaviour'
-
when 'plate' then 'PlateBehaviour'
-
when 'multiplexed_library' then 'MultiplexedLibraryBehaviour'
-
else raise StandardError, "Unknown core behaviour (#{self.asset_type.inspect}) for sample manifest"
-
end
-
-
core = rapid_generation? ? 'RapidCore' : 'Core'
-
@core_behaviour = "::SampleManifest::#{behaviour}::#{core}".constantize.new(self)
-
end
-
private :core_behaviour
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
1
module SampleManifest::Headers
-
-
1
def self.valid?(name)
-
METADATA_ATTRIBUTES_TO_CSV_COLUMNS.has_value?(name) || CORE_FIELDS.include?(name)
-
end
-
-
1
def self.renamed(h)
-
RENAMED[h]||h
-
end
-
-
# If a field name changes (Such as when it changes from optional to required)
-
# remap it here to preserve compatibility with older manifests
-
1
RENAMED = {
-
'DONOR ID (required for cancer samples)'=>'DONOR ID (required for EGA)',
-
'PHENOTYPE' => 'PHENOTYPE (required for EGA)'
-
}
-
-
# Used in a number of places, pulled out as not immediately obvious
-
1
TAG_GROUP_FIELD = 'TAG GROUP'
-
1
TAG2_GROUP_FIELD = 'TAG2 GROUP (Fill in for dual Index Only)'
-
1
TAG2_INDEX_FIELD = 'TAG2 INDEX (Fill in for dual Index Only)'
-
-
-
1
CORE_FIELDS = [
-
'SANGER PLATE ID',
-
'SANGER TUBE ID',
-
'WELL',
-
'SANGER SAMPLE ID',
-
'IS SAMPLE A CONTROL?',
-
'IS RE-SUBMITTED SAMPLE?',
-
TAG_GROUP_FIELD,
-
'TAG INDEX',
-
TAG2_GROUP_FIELD,
-
TAG2_INDEX_FIELD,
-
'LIBRARY TYPE',
-
'INSERT SIZE FROM',
-
'INSERT SIZE TO'
-
]
-
-
1
METADATA_ATTRIBUTES_TO_CSV_COLUMNS = {
-
:cohort => 'COHORT',
-
:gender => 'GENDER',
-
:father => 'FATHER (optional)',
-
:mother => 'MOTHER (optional)',
-
:sibling => 'SIBLING (optional)',
-
:country_of_origin => 'COUNTRY OF ORIGIN',
-
:geographical_region => 'GEOGRAPHICAL REGION',
-
:ethnicity => 'ETHNICITY',
-
:dna_source => 'DNA SOURCE',
-
:date_of_sample_collection => 'DATE OF SAMPLE COLLECTION (MM/YY or YYYY only)',
-
:date_of_sample_extraction => 'DATE OF DNA EXTRACTION (MM/YY or YYYY only)',
-
:sample_extraction_method => 'DNA EXTRACTION METHOD',
-
:sample_purified => 'SAMPLE PURIFIED?',
-
:purification_method => 'PURIFICATION METHOD',
-
:concentration => "CONC. (ng/ul)",
-
:concentration_determined_by => 'CONCENTRATION DETERMINED BY',
-
:sample_taxon_id => 'TAXON ID',
-
:sample_description => 'SAMPLE DESCRIPTION',
-
:accession_number_from_manifest => 'SAMPLE ACCESSION NUMBER (optional)',
-
:sample_sra_hold => 'SAMPLE VISIBILITY',
-
:sample_type => 'SAMPLE TYPE',
-
:volume => "VOLUME (ul)",
-
:sample_storage_conditions => 'DNA STORAGE CONDITIONS',
-
:supplier_name => 'SUPPLIER SAMPLE NAME',
-
:gc_content => 'GC CONTENT',
-
:sample_public_name => 'PUBLIC NAME',
-
:sample_common_name => 'COMMON NAME',
-
:sample_strain_att => 'STRAIN',
-
:donor_id => 'DONOR ID (required for EGA)',
-
:phenotype => 'PHENOTYPE (required for EGA)',
-
:genotype => 'GENOTYPE',
-
:age => 'AGE (with units)',
-
:developmental_stage => 'Developmental stage',
-
:cell_type => 'Cell Type',
-
:disease_state => 'Disease State',
-
:compound => 'Compound',
-
:dose => 'Dose',
-
:immunoprecipitate => 'Immunoprecipitate',
-
:growth_condition => 'Growth condition',
-
:rnai => 'RNAi',
-
:organism_part => 'Organism part',
-
:time_point => 'Time Point',
-
:treatment => 'Treatment',
-
:subject => 'Subject',
-
:disease => 'Disease',
-
:reference_genome_name => 'REFERENCE GENOME'
-
}
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
module SampleManifest::InputBehaviour
-
-
1
module ClassMethods
-
1
def find_sample_manifest_from_uploaded_spreadsheet(spreadsheet_file)
-
csv = CSV.parse(spreadsheet_file.read)
-
column_map = compute_column_map(csv[spreadsheet_header_row])
-
-
spreadsheet_offset.upto(csv.size-1) do |n|
-
sanger_sample_id = SampleManifest.read_column_by_name(csv, n, 'SANGER SAMPLE ID', column_map)
-
next if sanger_sample_id.blank?
-
sample = Sample.find_by_sanger_sample_id(sanger_sample_id) or next
-
return sample.sample_manifest
-
end
-
nil
-
end
-
-
1
def read_column_by_name(csv, row, name, column_map, default_value=nil)
-
col = column_map[name]
-
return default_value unless col
-
return csv[row][col]
-
end
-
-
1
def compute_column_map(names)
-
Hash[names.each_with_index.map { |name, index| [name && name.strip.gsub(/\s+/," "), index]}].tap do |columns|
-
raise StandardError, "No 'SANGER SAMPLE ID' column in #{columns.keys.inspect}" unless columns.key?('SANGER SAMPLE ID')
-
end
-
end
-
end
-
-
1
module SampleUpdating
-
1
module MetadataRules
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ValidationStateGuard
-
1
validation_guard(:updating_from_manifest)
-
-
# These need to be checked when updating from a sample manifest. We need to be able to display
-
# the sample ID so this can't be done with validates_presence_of
-
1
validates_each(:volume, :concentration, :if => :updating_from_manifest?) do |record, attr, value|
-
record.errors.add_on_blank(attr, message:"can't be blank for #{record.sample.sanger_sample_id}")
-
end
-
-
end
-
-
1
def accession_number_from_manifest=(new_value)
-
self.sample_ebi_accession_number ||= new_value
-
if new_value.present? && new_value != sample_ebi_accession_number
-
self.errors.add(:sample_ebi_accession_number, "can not be changed")
-
raise ActiveRecord::RecordInvalid, self
-
end
-
end
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ValidationStateGuard
-
-
# You cannot create a sample through updating the sample manifest
-
1
validates_each(:id, :on => :create, :if => :updating_from_manifest?) do |record, attr, value|
-
record.errors.add(:base,"Could not find sample #{record.sanger_sample_id}") if value.blank?
-
end
-
-
# We ensure that certain fields are updated properly if we're doing so through a manifest
-
1
before_validation(:if => :updating_from_manifest?) do |record|
-
if record.sample_supplier_name_empty?(record.sample_metadata.supplier_name)
-
record.reset_all_attributes_to_previous_values
-
record.empty_supplier_sample_name = true
-
record.generate_no_update_event
-
else
-
record.empty_supplier_sample_name = false
-
record.updated_by_manifest = true
-
end
-
end
-
-
# If the sample has already been updated by a manifest, and we're not overriding it
-
# then we should reset the sample information
-
1
before_validation(:if => :updating_from_manifest?) do |record|
-
record.reset_all_attributes_to_previous_values unless record.can_override_previous_manifest?
-
end
-
-
# We need to record any updates if we're working through a manifest update
-
1
attr_accessor :user_performing_manifest_update
-
1
after_save(:handle_update_event, :if => :updating_from_manifest?)
-
-
# The validation guards need declaring last so that they are reset after all of the after_save
-
# callbacks that may need them are executed.
-
1
validation_guard(:updating_from_manifest)
-
1
validation_guard(:override_previous_manifest)
-
end
-
-
# Modify the metadata so that it does the right checks when we are updating from a manifest
-
1
base::Metadata.class_eval do
-
1
include MetadataRules
-
end
-
end
-
-
1
def handle_update_event
-
events.updated_using_sample_manifest!(user_performing_manifest_update) unless @generate_no_update_event
-
user_performing_manifest_update = nil
-
end
-
1
private :handle_update_event
-
-
1
def generate_no_update_event
-
@generate_no_update_event = true
-
end
-
-
1
def can_override_previous_manifest?
-
# Have to use the previous value of 'updated_by_manifest' here as it may have been changed by
-
# the current update.
-
not self.updated_by_manifest_was or override_previous_manifest?
-
end
-
-
# Resets all of the attributes to their previous values
-
1
def reset_all_attributes_to_previous_values
-
reload unless new_record?
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
include ManifestUtil
-
1
extend ClassMethods
-
1
handle_asynchronously :process
-
-
# Ensure that we can override previous manifest information when required
-
1
extend ValidationStateGuard
-
1
validation_guard(:override_previous_manifest)
-
-
# Ensure that we can update the samples of a manifest
-
1
has_many :samples
-
1
accepts_nested_attributes_for :samples
-
1
alias_method_chain(:update_attributes!, :sample_manifest)
-
end
-
end
-
-
1
def convert_yes_no_to_boolean(value)
-
!!(value && value.match(/Y/i))
-
end
-
1
private :convert_yes_no_to_boolean
-
-
1
def clean_up_value(value)
-
return "" if value.nil?
-
value.strip
-
end
-
1
private :clean_up_value
-
-
1
def clean_up_sheet(csv)
-
# Clean up CSV
-
0.upto(csv.size-1) do |row|
-
0.upto(csv[row].size) do |col|
-
csv[row][col] = clean_up_value(csv[row][col])
-
end
-
end
-
csv
-
end
-
1
private :clean_up_sheet
-
-
1
def strip_non_word_characters(value)
-
return "" if value.nil?
-
value.gsub(/[^:alnum:]+/, '')
-
end
-
1
private :strip_non_word_characters
-
-
1
InvalidManifest = Class.new(StandardError)
-
-
1
def get_headers(csv)
-
filter_end_of_header(csv[spreadsheet_header_row]).map do |header|
-
h = header.gsub(/\s+/, ' ')
-
SampleManifest::Headers.renamed(h)
-
end
-
end
-
-
1
def each_csv_row(&block)
-
csv = CSV.parse(uploaded.current_data)
-
clean_up_sheet(csv)
-
-
headers = get_headers(csv)
-
-
headers.each_with_index.map do |name, index|
-
"Header '#{name}' not recognised!" unless name.blank? || SampleManifest::Headers.valid?(name)
-
end.compact.tap do |headers_with_errors|
-
raise InvalidManifest, headers_with_errors unless headers_with_errors.empty?
-
end
-
-
column_map = SampleManifest.compute_column_map(headers)
-
spreadsheet_offset.upto(csv.size-1) do |row|
-
yield(Hash[headers.each_with_index.map { |header, column| [ header, csv[row][column] ] }])
-
end
-
rescue CSV::MalformedCSVError => exception
-
raise InvalidManifest, "Invalid CSV file, did you upload an Excel file by accident? - #{exception.message}"
-
end
-
1
private :each_csv_row
-
-
# Always allow 'empty' samples to be updated, but non-empty samples need to have the override checkbox set for an update to occur
-
1
def process(user_updating_manifest, override_sample_information = false)
-
self.start!
-
-
samples_to_updated_attributes, sample_errors = [], []
-
each_csv_row do |row|
-
sanger_sample_id = row['SANGER SAMPLE ID']
-
next if sanger_sample_id.blank?
-
-
# Sanity check that the sample being updated is in the same container that it was defined against.
-
#
-
# NOTE: Do not include the primary_receptacle here as it will cause the wrong one to be loaded!
-
sample = samples.find_by_sanger_sample_id(sanger_sample_id)
-
-
errors = false
-
-
if sample.nil?
-
sample_errors.push("Sample #{sanger_sample_id} does not appear to be part of this manifest")
-
errors = true
-
elsif sample.primary_receptacle.nil?
-
sample_errors.push("Sample #{sanger_sample_id} appears to not have a receptacle defined! Contact PSD")
-
errors = true
-
else
-
validate_sample_container(sample, row) do |message|
-
sample_errors.push(message)
-
errors = true
-
end
-
validate_specialized_fields(sample,row) do |message|
-
sample_errors.push(message)
-
errors = true
-
end
-
end
-
-
next if errors
-
-
metadata = Hash[
-
SampleManifest::Headers::METADATA_ATTRIBUTES_TO_CSV_COLUMNS.map do |attribute, csv_column|
-
[ attribute, row[csv_column] ]
-
end
-
].merge(
-
:is_resubmitted => convert_yes_no_to_boolean(row['IS RE-SUBMITTED SAMPLE?'])
-
)
-
-
samples_to_updated_attributes.push([
-
sample.id, {
-
:id => sample.id,
-
:sanger_sample_id => sanger_sample_id,
-
:control => convert_yes_no_to_boolean(row['IS SAMPLE A CONTROL?']),
-
:sample_metadata_attributes => metadata.delete_if { |_,v| v.nil? }
-
}.merge( specialized_fields(row) )
-
])
-
end
-
-
raise InvalidManifest, sample_errors unless sample_errors.empty?
-
-
ActiveRecord::Base.transaction do
-
update_attributes!({
-
:override_previous_manifest => override_sample_information,
-
:samples_attributes => samples_to_updated_attributes.map(&:last)
-
}, user_updating_manifest)
-
core_behaviour.updated_by!(user_updating_manifest, samples_to_updated_attributes.map(&:first).compact)
-
end
-
-
self.last_errors = nil
-
self.finished!
-
rescue ActiveRecord::RecordInvalid => exception
-
errors.add(:base,exception.message)
-
fail_with_errors!(errors.full_messages)
-
rescue InvalidManifest => exception
-
fail_with_errors!(Array(exception.message).flatten)
-
end
-
-
1
def fail_with_errors!(errors)
-
reload
-
self.last_errors = errors
-
self.fail!
-
end
-
1
private :fail_with_errors!
-
-
1
def ensure_samples_are_being_updated_by_manifest(attributes, user)
-
attributes.fetch(:samples_attributes, []).each do |sample_attributes|
-
sample_attributes.merge!(
-
:updating_from_manifest => true,
-
:can_rename_sample => true,
-
:user_performing_manifest_update => user,
-
:override_previous_manifest => (override_previous_manifest? || attributes[:override_previous_manifest])
-
)
-
sample_attributes[:sample_metadata_attributes].delete_if { |_,v| v.nil? }
-
sample_attributes[:sample_metadata_attributes][:updating_from_manifest] = true
-
end
-
end
-
1
private :ensure_samples_are_being_updated_by_manifest
-
-
1
def update_attributes_with_sample_manifest!(attributes, user = nil)
-
ActiveRecord::Base.transaction do
-
ensure_samples_are_being_updated_by_manifest(attributes, user)
-
update_attributes_without_sample_manifest!(attributes)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module SampleManifest::MultiplexedLibraryBehaviour
-
module ClassMethods
-
def create_for_multiplexed_library!(attributes, *args, &block)
-
create!(attributes.merge(:asset_type => 'multiplexed_library'), *args, &block).tap do |manifest|
-
manifest.generate
-
end
-
end
-
end
-
-
class Core
-
def initialize(manifest)
-
@manifest = manifest
-
end
-
-
delegate :generate_mx_library, :to => :@manifest
-
-
-
def generate
-
@mx_tube = generate_mx_library
-
end
-
1
-
delegate :samples, :to => :@manifest
-
-
1
def io_samples
-
samples.map do |sample|
-
{
-
:sample => sample,
-
:container => {
-
:barcode => sample.primary_receptacle.sanger_human_barcode
-
},
-
:library_information => sample.primary_receptacle.library_information
-
}
-
end
-
end
-
-
1
def print_labels(&block)
-
label = [self.samples.first,self.samples.last].map(&:sanger_sample_id).join('-')
-
printables = [PrintBarcode::Label.new(
-
:number => multiplexed_library_tube.barcode,
-
:study => label,
-
:prefix => multiplexed_library_tube.prefix, :suffix => ""
-
)]
-
yield(printables, 'NT')
-
end
-
-
1
def multiplexed_library_tube
-
@mx_tube || raise("Mx tube not found")
-
end
-
-
1
def updated_by!(user, samples)
-
# Does nothing at the moment
-
end
-
-
1
def details(&block)
-
samples.each do |sample|
-
yield({
-
:barcode => sample.assets.first.sanger_human_barcode,
-
:sample_id => sample.sanger_sample_id
-
})
-
end
-
end
-
-
1
def validate_sample_container(sample, row, &block)
-
manifest_barcode, primary_barcode = row['SANGER TUBE ID'], sample.primary_receptacle.sanger_human_barcode
-
return if primary_barcode == manifest_barcode
-
yield("You can not move samples between tubes. #{sample.sanger_sample_id} is supposed to be in '#{primary_barcode}'' but has been moved to '#{manifest_barcode}'.")
-
end
-
-
1
def required_fields
-
['TAG INDEX','LIBRARY TYPE','INSERT SIZE FROM','INSERT SIZE TO']
-
end
-
-
1
def numeric_fields
-
['INSERT SIZE FROM','INSERT SIZE TO']
-
end
-
-
# Chances are we're going to use the same tag group multiple times. This avoids the need to poll
-
# the database each time, allowing us just to retrieve the list of tags in one go.
-
1
def tag_group_cache(name)
-
@tag_group_cache ||= Hash.new {|h,new_name| h[new_name] = TagGroup.include_tags.find(:first,:conditions => {:name=>new_name}) }
-
@tag_group_cache[name]
-
end
-
-
# There are a lot of things that can go wrong here
-
1
def validate_specialized_fields(sample,row,&block)
-
-
required_fields.each do |field|
-
yield "#{sample.sanger_sample_id} has no #{field.downcase} specified." if row[field].blank?
-
end
-
-
numeric_fields.each do |field|
-
yield "#{sample.sanger_sample_id} #{field.downcase} should be a number." unless /^[0-9]+$/ === row[field].strip
-
yield "#{sample.sanger_sample_id} #{field.downcase} should be greater than 0." unless row[field].to_i > 0
-
end
-
-
yield "Couldn't find the library type #{row['LIBRARY TYPE']} for #{sample.sanger_sample_id}." if LibraryType.find_by_name(row['LIBRARY TYPE']).nil?
-
-
return yield "#{sample.sanger_sample_id} has no tag group specified." if row[SampleManifest::Headers::TAG_GROUP_FIELD].blank?
-
-
# Tag Group validation
-
tag_group = tag_group_cache(row[SampleManifest::Headers::TAG_GROUP_FIELD])
-
return yield "Couldn't find a tag group called '#{row[SampleManifest::Headers::TAG_GROUP_FIELD]}'" if tag_group.nil?
-
yield "#{tag_group.name} doesn't include a tag with index #{row['TAG INDEX']}" if tag_group.tags.detect {|tag| tag.map_id == row['TAG INDEX'].to_i}.nil?
-
-
# Keep track if our first row is dual indexed or not.
-
@dual_indexed = row[SampleManifest::Headers::TAG2_GROUP_FIELD].present? if @dual_indexed.nil?
-
return yield "All samples in pool must have the same number of tags" unless @dual_indexed == row[SampleManifest::Headers::TAG2_GROUP_FIELD].present?
-
return unless @dual_indexed
-
-
tag2_group = tag_group_cache(row[SampleManifest::Headers::TAG2_GROUP_FIELD])
-
return yield "Couldn't find a tag group called '#{row[SampleManifest::Headers::TAG_GROUP_FIELD]}' for tag 2" if tag2_group.nil?
-
yield "#{tag2_group.name} doesn't include a tag with index #{row[SampleManifest::Headers::TAG2_INDEX_FIELD]}" if tag2_group.tags.detect {|tag| tag.map_id == row[SampleManifest::Headers::TAG2_INDEX_FIELD].to_i}.nil?
-
-
end
-
-
1
def specialized_fields(row)
-
-
tag_group = tag_group_cache(row[SampleManifest::Headers::TAG_GROUP_FIELD])
-
-
{
-
:specialized_from_manifest => {
-
:tag_id => tag_group.tags.detect {|tag| tag.map_id == row['TAG INDEX'].to_i}.id,
-
:library_type => row['LIBRARY TYPE'],
-
:insert_size_from => row['INSERT SIZE FROM'].to_i,
-
:insert_size_to => row['INSERT SIZE TO'].to_i
-
}
-
}.tap do |params|
-
if row[SampleManifest::Headers::TAG2_GROUP_FIELD].present?
-
tag2_group = tag_group_cache(row[SampleManifest::Headers::TAG2_GROUP_FIELD])
-
params[:specialized_from_manifest].merge!(:tag2_id => tag2_group.tags.detect {|tag| tag.map_id == row[SampleManifest::Headers::TAG2_INDEX_FIELD].to_i}.id )
-
end
-
end
-
end
-
end
-
-
1
RapidCore = Core
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
end
-
end
-
-
1
def generate_mx_library
-
tubes = generate_tubes(Tube::Purpose.standard_library_tube)
-
Tube::Purpose.standard_mx_tube.create!.tap do |mx_tube|
-
RequestFactory.create_external_multiplexed_library_creation_requests(tubes, mx_tube, study)
-
end
-
end
-
-
1
def sample_tube_sample_creation(samples_data,study_id)
-
study.samples << samples_data.map do |barcode, sanger_sample_id, prefix|
-
create_sample(sanger_sample_id).tap do |sample|
-
sample_tube = LibraryTube.find_by_barcode(barcode) or raise ActiveRecord::RecordNotFound, "Cannot find library tube with barcode #{barcode.inspect}"
-
sample_tube.aliquots.create!(:sample => sample)
-
end
-
end
-
end
-
1
private :sample_tube_sample_creation
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014 Genome Research Ltd.
-
module SampleManifest::PlateBehaviour
-
module ClassMethods
-
def create_for_plate!(attributes, *args, &block)
-
create!(attributes.merge(:asset_type => 'plate'), *args, &block).tap do |manifest|
-
manifest.generate
-
end
-
end
-
end
-
-
class Base
-
-
include SampleManifest::CoreBehaviour::NoSpecializedValidation
-
-
def initialize(manifest)
-
@manifest = manifest
-
end
-
-
delegate :generate_plates, :to => :@manifest
-
alias_method(:generate, :generate_plates)
-
-
delegate :samples, :to => :@manifest
-
-
def print_labels_for(plates, &block)
-
plates = plates.sort_by(&:barcode)
-
stock_plate_purpose = PlatePurpose.stock_plate_purpose
-
yield(plates.map(&:barcode_label_for_printing), Plate.prefix, "long", stock_plate_purpose.name.to_s)
-
end
-
-
# This method ensures that each of the plates is handled by an individual job. If it doesn't do this we run
-
# the risk that the 'handler' column in the database for the delayed job will not be large enough and will
-
# truncate the data.
-
def generate_wells_for_plates(well_data, plates, &block)
-
cloned_well_data = well_data.dup
-
plates.each do |plate|
-
block.call(
-
cloned_well_data.slice!(0, plate.size),
-
plate
-
)
-
end
-
end
-
private :generate_wells_for_plates
-
-
def validate_sample_container(sample, row, &block)
-
manifest_barcode, manifest_location = row['SANGER PLATE ID'], row['WELL']
-
primary_barcode, primary_location = sample.primary_receptacle.plate.sanger_human_barcode, sample.primary_receptacle.map.description
-
return if primary_barcode == manifest_barcode and primary_location == manifest_location
-
yield("You can not move samples between wells or modify barcodes: #{sample.sanger_sample_id} should be in '#{primary_barcode} #{primary_location}' but the manifest is trying to put it in '#{manifest_barcode} #{manifest_location}'")
-
end
-
end
-
-
#--
-
# This class is only used by the UI version of Sequencescape and so it only supports a subset of
-
# the methods required. It can be used to generate the Excel file and to print the labels but it
-
# could not be used for the API not for handling the uploaded sample manifest CSV file.
-
#++
-
class RapidCore < Base
-
def generate_wells(well_data, plates)
-
# Generate the wells, samples & requests asynchronously.
-
generate_wells_for_plates(well_data, plates) do |this_plates_well_data, plate|
-
@manifest.generate_wells_asynchronously(
-
this_plates_well_data.map { |map,sample_id| [map.id, sample_id] },
-
plate.id
-
)
-
end
-
-
# Ensure we maintain the information we need for printing labels and generating
-
# the CSV file
-
@plates = plates.sort_by(&:barcode)
-
@details = []
-
plates.each do |plate|
-
well_data.slice!(0, plate.size).each do |map,sample_id|
-
@details << {
-
:barcode => plate.sanger_human_barcode,
-
:position => map.description,
-
:sample_id => sample_id
-
}
-
end
-
end
-
end
-
-
def print_labels(&block)
-
print_labels_for(@plates, &block)
-
end
-
-
def details(&block)
-
@details.map(&block.method(:call))
-
end
-
end
-
-
class Core < Base
-
def generate_wells(well_data, plates)
-
generate_wells_for_plates(well_data, plates, &@manifest.method(:generate_wells))
-
end
-
-
def io_samples
-
samples.map do |sample|
-
container = sample.primary_receptacle
-
{
-
:sample => sample,
-
:container => {
-
:barcode => container.plate.sanger_human_barcode,
-
:position => container.map.description.sub(/^([^\d]+)(\d)$/, '\10\2')
-
}
-
}
-
end
-
end
-
-
def print_labels(&block)
-
print_labels_for(self.samples.map { |s| s.primary_receptacle.plate }.uniq, &block)
-
end
-
-
def updated_by!(user, samples)
-
# It's more efficient to look for the wells with the samples than to look for the assets from the samples
-
# themselves as the former can use named_scopes where as the latter is an array that needs iterating over.
-
Plate.with_sample(samples).each do |plate|
-
plate.events.updated_using_sample_manifest!(user)
-
end
-
end
-
-
def details(&block)
-
samples.each do |sample|
-
well = sample.wells.first(:include => [ :container, :map ])
-
yield({
-
:barcode => well.plate.sanger_human_barcode,
-
:position => well.map.description,
-
:sample_id => sample.sanger_sample_id
-
})
-
end
-
end
-
end
-
-
def self.included(base)
-
base.class_eval do
-
extend ClassMethods
-
1
-
delegate :stock_plate_purpose, :to => 'PlatePurpose'
-
end
-
end
-
-
def generate_wells_asynchronously(well_data_with_ids, plate_id)
-
ActiveRecord::Base.transaction do
-
# Ensure the order of the wells are maintained
-
maps = Hash[Map.find(well_data_with_ids.map(&:first)).map { |map| [ map.id, map ] }]
-
well_data = well_data_with_ids.map { |map_id,sample_id| [ maps[map_id], sample_id ] }
-
-
generate_wells(well_data, Plate.find(plate_id))
-
end
-
end
-
handle_asynchronously :generate_wells_asynchronously
-
-
def generate_plates
-
study_abbreviation = self.study.abbreviation
-
-
well_data = []
-
plates = (0...self.count).map do |_|
-
Plate.create_with_barcode!(:plate_purpose => stock_plate_purpose)
-
end.sort_by(&:barcode).map do |plate|
-
plate.tap do |plate|
-
sanger_sample_ids = generate_sanger_ids(plate.size)
-
-
Map.walk_plate_in_column_major_order(plate.size) do |map, _|
-
sanger_sample_id = sanger_sample_ids.shift
-
generated_sanger_sample_id = SangerSampleId.generate_sanger_sample_id!(study_abbreviation, sanger_sample_id)
-
-
well_data << [map, generated_sanger_sample_id]
-
end
-
end
-
end
-
core_behaviour.generate_wells(well_data, plates)
-
self.barcodes = plates.map(&:sanger_human_barcode)
-
-
save!
-
end
-
-
def generate_wells(wells_for_plate, plate)
-
study.samples << wells_for_plate.map do |map,sanger_sample_id|
-
create_sample(sanger_sample_id).tap do |sample|
-
plate.wells.create!(:map => map, :well_attribute => WellAttribute.new).tap do |well|
-
well.aliquots.create!(:sample => sample)
-
end
-
end
-
end
-
-
plate.events.created_using_sample_manifest!(self.user)
-
-
RequestFactory.create_assets_requests(plate.wells, study)
-
end
-
private :generate_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013 Genome Research Ltd.
-
module SampleManifest::SampleTubeBehaviour
-
module ClassMethods
-
def create_for_sample_tube!(attributes, *args, &block)
-
create!(attributes.merge(:asset_type => '1dtube'), *args, &block).tap do |manifest|
-
manifest.generate
-
end
-
end
-
end
-
-
class Core
-
-
include SampleManifest::CoreBehaviour::NoSpecializedValidation
-
-
def initialize(manifest)
-
@manifest = manifest
-
end
-
-
delegate :generate_1dtubes, :to => :@manifest
-
alias_method(:generate, :generate_1dtubes)
-
1
-
delegate :samples, :to => :@manifest
-
-
1
def io_samples
-
samples.map do |sample|
-
{
-
:sample => sample,
-
:container => {
-
:barcode => sample.primary_receptacle.sanger_human_barcode
-
}
-
}
-
end
-
end
-
-
1
def print_labels(&block)
-
printables = self.samples.map do |sample|
-
sample_tube = sample.assets.first
-
PrintBarcode::Label.new(
-
:number => sample_tube.barcode,
-
:study => sample.sanger_sample_id,
-
:prefix => sample_tube.prefix, :suffix => ""
-
)
-
end
-
yield(printables, 'NT')
-
end
-
-
1
def updated_by!(user, samples)
-
# Does nothing at the moment
-
end
-
-
1
def details(&block)
-
samples.each do |sample|
-
yield({
-
:barcode => sample.assets.first.sanger_human_barcode,
-
:sample_id => sample.sanger_sample_id
-
})
-
end
-
end
-
-
1
def validate_sample_container(sample, row, &block)
-
manifest_barcode, primary_barcode = row['SANGER TUBE ID'], sample.primary_receptacle.sanger_human_barcode
-
return if primary_barcode == manifest_barcode
-
yield("You cannot move samples between tubes or modify their barcodes: #{sample.sanger_sample_id} should be in '#{primary_barcode}' but the manifest is trying to put it in '#{manifest_barcode}'")
-
end
-
-
end
-
-
# There is no reason for this to need a rapid version as it should be reasonably
-
# efficient in the first place.
-
1
RapidCore = Core
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
end
-
end
-
-
1
def generate_1dtubes
-
generate_tubes(Tube::Purpose.standard_sample_tube)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
1
module SampleManifest::SharedTubeBehaviour
-
1
def generate_tubes(purpose)
-
sanger_ids = generate_sanger_ids(self.count)
-
study_abbreviation = self.study.abbreviation
-
-
tubes, samples_data = [], []
-
(0...self.count).each do |_|
-
tube = purpose.create!
-
sanger_sample_id = SangerSampleId.generate_sanger_sample_id!(study_abbreviation, sanger_ids.shift)
-
-
tubes << tube
-
samples_data << [tube.barcode, sanger_sample_id, tube.prefix]
-
end
-
-
self.barcodes = tubes.map(&:sanger_human_barcode)
-
-
tube_sample_creation(samples_data,self.study.id)
-
delayed_generate_asset_requests(tubes.map(&:id), self.study.id)
-
save!
-
tubes
-
end
-
-
1
def delayed_generate_asset_requests(asset_ids,study_id)
-
# TODO: Refactor?
-
RequestFactory.create_assets_requests(Asset.find(asset_ids), Study.find(study_id))
-
end
-
1
handle_asynchronously :delayed_generate_asset_requests
-
-
-
1
def tube_sample_creation(samples_data,study_id)
-
study.samples << samples_data.map do |barcode, sanger_sample_id, prefix|
-
create_sample(sanger_sample_id).tap do |sample|
-
sample_tube = Tube.find_by_barcode(barcode) or raise ActiveRecord::RecordNotFound, "Cannot find sample tube with barcode #{barcode.inspect}"
-
sample_tube.aliquots.create!(:sample => sample)
-
end
-
end
-
end
-
1
private :tube_sample_creation
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
-
1
require 'aasm'
-
-
1
module SampleManifest::StateMachine
-
1
def self.extended(base)
-
1
base.class_eval do
-
1
include AASM
-
-
1
configure_state_machine
-
end
-
end
-
-
1
def configure_state_machine
-
1
aasm_column :state
-
1
aasm_state :pending
-
1
aasm_state :processing
-
1
aasm_state :failed
-
1
aasm_state :completed
-
1
aasm_initial_state :pending
-
-
# State Machine events
-
1
aasm_event :start do
-
1
transitions :to => :processing, :from => [:pending, :failed, :completed, :processing]
-
end
-
-
1
aasm_event :finished do
-
1
transitions :to => :completed, :from => [:processing]
-
end
-
-
1
aasm_event :fail do
-
1
transitions :to => :failed, :from => [:processing]
-
end
-
end
-
1
private :configure_state_machine
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
module SampleManifest::TemplateBehaviour
-
1
def applicable_templates
-
return SampleManifestTemplate.all unless self.asset_type.present?
-
SampleManifestTemplate.all(:conditions => [ 'asset_type=? OR asset_type IS NULL', asset_type ], :order => 'asset_type DESC')
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2015 Genome Research Ltd.
-
1
class SampleManifestTemplate < ActiveRecord::Base
-
1
serialize :default_values, Hash
-
1
serialize :cell_map, Hash
-
-
1
def self.populate()
-
transaction do
-
map = {
-
:study => [4,1],
-
:supplier => [5,1],
-
:number_of_plates => [6,1]
-
}
-
SampleManifestTemplate.create!(
-
:name => "default layout",
-
:path => "/data/base_manifest.xls",
-
:cell_map => map,
-
:asset_type => 'plate'
-
)
-
SampleManifestTemplate.create!(
-
:name => "full layout",
-
:path => "/data/full_manifest.xls",
-
:cell_map => map,
-
:asset_type => 'plate'
-
)
-
SampleManifestTemplate.create!(
-
:name => "default tube layout",
-
:path => "/data/base_tube_manifest.xls",
-
:cell_map => map,
-
:asset_type => '1dtube'
-
)
-
SampleManifestTemplate.create!(
-
:name => "full tube layout",
-
:path => "/data/full_tube_manifest.xls",
-
:cell_map => map,
-
:asset_type => '1dtube'
-
)
-
SampleManifestTemplate.create!(
-
:name => "relevant RNA/ChIP",
-
:path => "/data/relevant_rnachip_plate_manifest.xls",
-
:cell_map => map,
-
:asset_type => '1dtube'
-
)
-
SampleManifestTemplate.create!(
-
:name=> 'Simple multiplexed library manifest',
-
:asset_type => 'multiplexed_library',
-
:path => '/data/base_mx_library_manifest.xls',
-
:cell_map => {:study=>[4, 1], :supplier=>[5, 1], :number_of_plates=>[6, 1]}
-
)
-
-
unless Rails.env == "production"
-
SampleManifestTemplate.create!(
-
:name => "test layout",
-
:path => "/data/base2_manifest.xls",
-
:cell_map => {
-
:study => [3,1],
-
:supplier => [9,0],
-
:number_of_plates => [6,1]
-
},
-
:default_values => {
-
"GENDER" => "Male"
-
}
-
)
-
end
-
end
-
end
-
-
1
def read_column_position(manifest, worksheet)
-
Hash[worksheet.row(manifest.spreadsheet_header_row).each_with_index.map { |name, index| [name && name.strip.gsub(/\s+/," "), index] }]
-
end
-
1
private :read_column_position
-
-
1
def fill_row_with_default_values(worksheet, current_row, default_values)
-
return unless default_values
-
default_values.each do |key, value|
-
position = @column_position_map[key]
-
next unless position
-
worksheet[current_row, position] = value
-
end
-
end
-
1
private :fill_row_with_default_values
-
-
1
def set_value(worksheet, cell_name, value)
-
row_col = cell_map[cell_name]
-
return nil unless row_col
-
row, col = row_col
-
worksheet.row(row)[col] = value
-
return value
-
end
-
1
private :set_value
-
-
1
def generate(manifest)
-
spreadsheet = Spreadsheet.open(Rails.root.to_s + (self.path || '/data/base_manifest.xls'))
-
worksheet = spreadsheet.worksheets.first
-
-
@column_position_map = read_column_position(manifest, worksheet)
-
barcode_position = @column_position_map['SANGER PLATE ID']||@column_position_map['SANGER TUBE ID']
-
position_position = @column_position_map['WELL']
-
sample_id_position = @column_position_map['SANGER SAMPLE ID']
-
donor_id_position = @column_position_map['DONOR ID (required for EGA)']||@column_position_map['DONOR ID (required for cancer samples)']
-
-
set_value(worksheet, :study, manifest.study.abbreviation)
-
set_value(worksheet, :supplier, Supplier.find(manifest.supplier_id).name)
-
set_value(worksheet, :number_of_plates, manifest.count) # NOT 'number_of_plates' BUT number of things!
-
-
current_row = manifest.spreadsheet_offset
-
-
manifest.details do |details|
-
worksheet[current_row, barcode_position] = details[:barcode]
-
worksheet[current_row, sample_id_position] = details[:sample_id]
-
worksheet[current_row, position_position] = details[:position] if details.key?(:position)
-
worksheet[current_row, donor_id_position] = details[:sample_id]
-
fill_row_with_default_values(worksheet, current_row, default_values)
-
-
current_row = current_row + 1
-
end
-
-
# Truncate the number of rows in the spreadsheet. This improves performance dramatically because the
-
# number of rows in the original sheet is 9999, which means 20s of unnecessary data processing. This
-
# change causes times to drop to < 1s. An extra offset is required because Excel does things in blocks
-
# of 32 rows
-
worksheet.dimensions[1] = current_row + 64
-
Tempfile.open(File.basename(spreadsheet.io.path)) do |tempfile|
-
spreadsheet.write(tempfile.path) # Write out the spreadsheet
-
tempfile.open # Reopen the temporary file
-
manifest.update_attributes!(:generated => tempfile)
-
end
-
end
-
-
1
def create!(attributes = nil, &block)
-
attributes ||= {}
-
attributes[:asset_type] = self.asset_type if self.asset_type.present?
-
SampleManifest.create!(attributes, &block)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class SamplePrepQcTask < Task
-
1
class SamplePrepQcData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && SamplePrepQcData.new(request)
-
end
-
-
1
def partial
-
"sample_prep_qc_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_sample_prep_qc_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_sample_prep_qc_task(self, params)
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
require 'rexml/text'
-
# An instance of this class is responsible for the registration of a sample and its sample tube.
-
# You can think of this as a binding between those two, within the context of a user, study and
-
# asset group.
-
#
-
#--
-
# NOTE: This is very much a temporary object: after creation the instance will instantly destroy
-
# itself. This is primarily done because Rails 2.3 doesn't have the ActiveModel features of
-
# Rails 3, and we need some of those above-and-beyond just validation. If required, the after_create
-
# callback could be removed to keep track of sample registrations.
-
#++
-
1
class SampleRegistrar < ActiveRecord::Base
-
-
# UPGRADE TODO: This hack is horrible! Find out what its doing and fix it!
-
1
def initialize(attributes = {},what = {})
-
super({ :sample_attributes => {}, :sample_tube_attributes => {} }.merge(attributes.symbolize_keys),what)
-
end
-
-
# Raised if the call to SampleRegistrar.register! fails for any reason, and so that calling code
-
# can get at the SampleRegistrar instances that were in the process of being created.
-
1
class RegistrationError < StandardError
-
1
attr_reader :sample_registrars
-
-
1
def initialize(sample_registrars)
-
@sample_registrars = sample_registrars
-
end
-
end
-
-
1
class AssetGroupHelper
-
1
def initialize
-
@asset_groups = {}
-
end
-
-
1
def existing_asset_group?(name)
-
return @asset_groups[name] if @asset_groups.key?(name)
-
@asset_groups[name] = !AssetGroup.find_by_name(name).nil?
-
end
-
end
-
-
1
NoSamplesError = Class.new(RegistrationError)
-
-
# This method is the main registration interface, taking a list of attributes and registering the
-
# associated sample and sample tubes. You get back a list of SampleRegistrar instances. If anything
-
# goes wrong you get a RegistrationError raised.
-
1
def self.register!(registration_attributes)
-
# Note that we're explicitly ignoring the ignored records here!
-
-
helper = AssetGroupHelper.new
-
registrars = registration_attributes.map { |attributes| new(attributes.merge(:asset_group_helper => helper)) }.reject(&:ignore?)
-
raise NoSamplesError, registrars if registrars.empty?
-
begin
-
# We perform this in a database wide transaction because it is altering several tables. It also locks
-
# the tables from change whilst we validate our instances.
-
ActiveRecord::Base.transaction do
-
all_valid = registrars.inject(true) { |all_valid_so_far,registrar| registrar.valid? && all_valid_so_far }
-
raise RegistrationError, registrars unless all_valid
-
registrars.each { |registrar| registrar.save! }
-
end
-
-
return registrars
-
rescue ActiveRecord::RecordInvalid => exception
-
# NOTE: this shouldn't ever happen but you never know!
-
raise RegistrationError, registrars
-
end
-
end
-
-
# We are registering samples on the behalf of a specified user within a specified study
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
1
belongs_to :study
-
1
validates_presence_of :study
-
-
1
belongs_to :sample, :validate => true, :autosave => true
-
1
accepts_nested_attributes_for :sample
-
1
validates_presence_of :sample
-
-
1
after_create do |record|
-
# NOTE: this looks like it should be 'record.user.is_owner_of(record.sample)' but ActiveRecord and the
-
# dynamic methods associated with User and Role causes that not to work. So you have to be explicit
-
# and send the request for the method!
-
record.user.send(:is_owner_of, record.sample)
-
record.study.samples.concat(record.sample)
-
RequestFactory.create_assets_requests([record.sample_tube], record.study)
-
end
-
-
# Samples always come in a SampleTube when coming through us
-
1
belongs_to :sample_tube, :validate => true, :autosave => true
-
1
accepts_nested_attributes_for :sample_tube
-
1
validates_presence_of :sample_tube
-
-
1
before_validation do |record|
-
record.sample_tube.name = record.sample.name
-
end
-
1
after_create do |record|
-
record.sample_tube.aliquots.create!(:sample => record.sample, :study=>record.study)
-
end
-
-
# SampleTubes are registered within an AssetGroup, unless the AssetGroup is unspecified.
-
1
attr_accessor :asset_group_helper
-
1
attr_accessor :asset_group_name
-
1
belongs_to :asset_group, :validate => true, :autosave => true
-
1
validates_each(:asset_group_name, :if => :new_record?) do |record, attr, value|
-
record.errors.add(:asset_group, "#{value} already exists, please enter another name") if record.asset_group_helper.existing_asset_group?(value)
-
end
-
-
1
before_create do |record|
-
record.asset_group = SampleRegistrar.create_asset_group_by_name(record.asset_group_name, record.study)
-
end
-
-
1
after_create do |record|
-
record.asset_group.assets.concat(record.sample_tube) unless record.asset_group.blank?
-
end
-
-
1
def self.create_asset_group_by_name(name, study)
-
return nil if name.blank?
-
AssetGroup.find_by_name(name) || AssetGroup.create!(:name => name, :study => study)
-
end
-
-
# This model does not really need to exist but, without Rails 3, we can't easily use the ActiveRecord stuff.
-
# So, once have created an instance we immediately destroy it. Note that, because of the way ActiveRecord
-
# works, this *must* be the LAST after_create callback in this file.
-
1
after_create { |record| record.delete }
-
-
# Is this instance to be ignored?
-
1
def ignore?
-
!!@ignore
-
end
-
1
alias_method :ignore, :ignore?
-
-
1
def ignore=(ignore)
-
@ignore = ('1' == ignore)
-
end
-
-
1
SpreadsheetError = Class.new(StandardError)
-
1
TooManySamplesError = Class.new(SpreadsheetError)
-
-
# Column names from old spreadsheets that need mapping to new names.
-
1
REMAPPED_COLUMN_NAMES = { 'Asset group name' => 'Asset group' }
-
-
# Columns that are required for the spreadsheet to be considered valid.
-
1
REQUIRED_COLUMNS = [ 'Asset group', 'Sample name' ]
-
3
REQUIRED_COLUMNS_SENTENCE = REQUIRED_COLUMNS.map { |w| "'#{w}'" }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ')
-
-
1
def self.from_spreadsheet(file, study, user)
-
workbook = Spreadsheet.open(file.path) or raise SpreadsheetError, 'Problems processing your file. Only Excel spreadsheets accepted'
-
worksheet = workbook.worksheet(0)
-
-
# Assume there is always 1 header row
-
num_samples = worksheet.count - 1
-
-
if num_samples > configatron.uploaded_spreadsheet.max_number_of_samples
-
raise TooManySamplesError, "You can only load #{configatron.uploaded_spreadsheet.max_number_of_samples} samples at a time. Please split the file into smaller groups of samples."
-
end
-
-
# Map the header from the spreadsheet (the first row) to the attributes of the sample registrar. Each column
-
# has the same text as the label for the attribute, once it has been HTML unescaped.
-
#
-
# NOTE: There are two different versions of the spreadsheet in the wild. One has a 'Volume' column name that
-
# needs to be decoded using CGI HTML unescaping (the old format), and the other needs the column decoded
-
# using the XML encoding (the new format). Every column is mapped using both encodings, with the XML version
-
# being the preferred decoding.
-
definitions = Sample::Metadata.attribute_details.inject({}) do |hash,attribute|
-
label = attribute.to_field_info.display_name
-
handler = ->(attributes, value) { attributes[:sample_attributes][:sample_metadata_attributes][attribute.name] = value }
-
hash.tap do
-
hash[CGI.unescapeHTML(label)] = handler # For the old spreadsheets
-
hash[REXML::Text.unnormalize(label)] = handler # For the new spreadsheets
-
end
-
end.merge(
-
'Asset group' => ->(attributes, value) { attributes[:asset_group_name] = value },
-
'Sample name' => ->(attributes, value) { attributes[:sample_attributes][:name] = value },
-
'2D barcode' => ->(attributes, value) { attributes[:sample_tube_attributes][:two_dimensional_barcode] = value },
-
'Reference Genome' => ->(attributes, value) { attributes[:sample_attributes][:sample_metadata_attributes][:reference_genome_id] = ReferenceGenome.find_by_name(value).try(:id) || 0 }
-
)
-
-
# Map the headers to their attribute handlers. Ensure that the required headers are present.
-
used_definitions, headers = [], []
-
column_index, column_name = 0, worksheet.cell(0, 0).to_s.gsub(/\000/,'').gsub(/\.0/,'').strip
-
until column_name.empty?
-
column_name = REMAPPED_COLUMN_NAMES.fetch(column_name, column_name)
-
handler = definitions[column_name]
-
unless handler.nil?
-
used_definitions[column_index] = handler
-
headers << column_name
-
end
-
-
column_index = column_index + 1
-
column_name = worksheet.cell(0, column_index).to_s.gsub(/\000/,'').gsub(/\.0/,'').strip
-
end
-
-
if (headers & REQUIRED_COLUMNS) != REQUIRED_COLUMNS
-
raise SpreadsheetError, "Please check that your spreadsheet is in the latest format: one of #{REQUIRED_COLUMNS_SENTENCE} is missing or in the wrong column."
-
end
-
-
# Build a SampleRegistrar instance for each row of the spreadsheet, mapping the cells of the
-
# spreadsheet to their appropriate attribute.
-
sample_registrars = []
-
1.upto(num_samples) do |row|
-
attributes = {
-
:asset_group_helper => SampleRegistrar::AssetGroupHelper.new,
-
:sample_attributes => {
-
:sample_metadata_attributes => { }
-
},
-
:sample_tube_attributes => { }
-
}
-
-
used_definitions.each_with_index do |handler, index|
-
next if handler.nil?
-
value = worksheet.cell(row,index).to_s.gsub(/\000/,'').gsub(/\.0/,'').strip
-
handler.call(attributes, value) unless value.blank?
-
end
-
next if attributes[:sample_attributes][:name].blank?
-
-
# Store the sample registration and check that it is valid. This will mean that the
-
# UI will display any errors without the user having to submit the form to find out.
-
-
SampleRegistrar.new(attributes.merge(:study => study, :user => user)).tap do |sample_registrar|
-
sample_registrars.push(sample_registrar)
-
sample_registrar.valid?
-
end
-
end
-
-
return sample_registrars
-
rescue Ole::Storage::FormatError => exception
-
raise SpreadsheetError, 'Problems processing your file. Only Excel spreadsheets accepted'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class SampleTube < Tube
-
1
include Api::SampleTubeIO::Extensions
-
1
include ModelExtensions::SampleTube
-
1
include StandardNamedScopes
-
-
1
after_create do |record|
-
record.barcode = AssetBarcode.new_barcode if record.barcode.blank?
-
record.name = record.primary_aliquot.sample.name if record.name.blank? and not record.primary_aliquot.try(:sample).nil?
-
-
record.save! if record.barcode_changed? or record.name_changed?
-
end
-
-
# All instances are labelled 'SampleTube', unless otherwise specified
-
1
before_validation do |record|
-
record.label = 'SampleTube' if record.label.blank?
-
end
-
-
1
def created_with_request_options
-
{}
-
end
-
-
1
def can_be_created?
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class SangerSampleId < ActiveRecord::Base
-
1
class Factory
-
1
def self.instance
-
@instance ||= self.new
-
end
-
-
1
def next!
-
SangerSampleId.create!.sample_id
-
end
-
end
-
-
1
alias_method(:sample_id, :id)
-
-
1
class << self
-
1
def generate_sanger_sample_id!(study_abbreviation, sanger_id = nil)
-
"#{study_abbreviation}#{sanger_id || SangerSampleId::Factory.instance.next!}"
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# Searching is really a behaviour separate from the thing you are search against. For instance, with
-
# a call to Asset.all you find all assets; except if we removed the asset hierarchy you do not get a search
-
# across all of the models that made it up. Instead you want a SearchForAsset model that does the correct
-
# search behaviour for you. Well, this is the base for that.
-
#
-
# You must implement a 'scope' method that takes a hash of the parameters as a parameter and returns a
-
# named scope like object (i.e. something the calling code can then call first, last, all or paginate on).
-
# It is not your search implementations responsibility to decide how many things are being searched for.
-
1
class Search < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Search::FindAssetByBarcode < Search
-
1
def scope(criteria)
-
Asset.with_machine_barcode(criteria['barcode'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
-
1
require "#{Rails.root.to_s}/app/models/illumina_htp/plate_purposes"
-
-
1
class Search::FindIlluminaAPlates < Search
-
1
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
Plate.include_plate_purpose.with_plate_purpose(illumina_a_plate_purposes).with_no_outgoing_transfers.in_state(criteria['state']).located_in(freezer)
-
end
-
-
1
def illumina_a_plate_purposes
-
PlatePurpose.find_all_by_name(IlluminaHtp::PlatePurposes::PLATE_PURPOSE_FLOWS.flatten)
-
end
-
1
private :illumina_a_plate_purposes
-
-
1
def freezer
-
Location.find_by_name('Illumina high throughput freezer') or raise ActiveRecord::RecordNotFound, 'Illumina high throughput freezer'
-
end
-
1
private :freezer
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class Search::FindIlluminaAStockPlates < Search::FindIlluminaAPlates
-
1
def illumina_a_plate_purposes
-
PlatePurpose.find_all_by_name(IlluminaHtp::PlatePurposes::STOCK_PLATE_PURPOSE)
-
end
-
1
private :illumina_a_plate_purposes
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
-
1
require "#{Rails.root.to_s}/app/models/illumina_b/plate_purposes"
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
1
class Search::FindIlluminaBPlates < Search
-
1
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
Plate.include_plate_purpose.with_plate_purpose(illumina_b_plate_purposes).with_no_outgoing_transfers.in_state(criteria['state']).located_in(freezer)
-
end
-
-
1
def illumina_b_plate_purposes
-
names = (IlluminaB::PlatePurposes::PLATE_PURPOSE_FLOWS + IlluminaHtp::PlatePurposes::PLATE_PURPOSE_FLOWS + Pulldown::PlatePurposes::ISCH_PURPOSE_FLOWS).flatten.uniq
-
PlatePurpose.find_all_by_name(names)
-
end
-
1
private :illumina_b_plate_purposes
-
-
1
def freezer
-
Location.find_by_name('Illumina high throughput freezer') or raise ActiveRecord::RecordNotFound, 'Illumina high throughput freezer'
-
end
-
1
private :freezer
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
-
1
require "#{Rails.root.to_s}/app/models/illumina_b/plate_purposes"
-
-
1
class Search::FindIlluminaBPlatesForUser < Search::FindIlluminaBPlates
-
1
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
super.for_user(Uuid.lookup_single_uuid(criteria['user_uuid']).resource)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
-
1
require "#{Rails.root.to_s}/app/models/illumina_b/plate_purposes"
-
-
1
class Search::FindIlluminaBStockPlates < Search::FindIlluminaBPlates
-
1
def illumina_b_plate_purposes
-
PlatePurpose.find_all_by_name([IlluminaB::PlatePurposes::STOCK_PLATE_PURPOSE,IlluminaHtp::PlatePurposes::STOCK_PLATE_PURPOSE].concat(Pulldown::PlatePurposes::STOCK_PLATE_PURPOSES))
-
end
-
1
private :illumina_b_plate_purposes
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2015 Genome Research Ltd.
-
-
require "#{Rails.root.to_s}/app/models/illumina_b/plate_purposes"
-
-
class Search::FindIlluminaBTubes < Search
-
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
Tube.include_purpose.
-
with_purpose(illumina_b_tube_purposes).
-
with_no_outgoing_transfers.
-
in_state(criteria['state']).
-
without_finished_tubes(illumina_b_final_tube_purpose).
-
recent_first
-
end
-
-
def self.illumina_b_tube_purposes
-
Tube::Purpose.find_all_by_name(IlluminaB::PlatePurposes::TUBE_PURPOSE_FLOWS.flatten)
-
end
-
delegate :illumina_b_tube_purposes, :to => 'self.class'
-
-
def self.illumina_b_final_tube_purpose
-
Tube::Purpose.find_all_by_name(IlluminaB::PlatePurposes::TUBE_PURPOSE_FLOWS.map(&:last))
-
1
end
-
delegate :illumina_b_final_tube_purpose, :to => 'self.class'
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2015 Genome Research Ltd.
-
-
1
require "#{Rails.root.to_s}/app/models/illumina_c/plate_purposes"
-
-
1
class Search::FindIlluminaCPlates < Search
-
1
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
Plate.include_plate_purpose.with_plate_purpose(illumina_c_plate_purposes).with_no_outgoing_transfers.in_state(criteria['state']).located_in(freezer)
-
end
-
-
1
def illumina_c_plate_purposes
-
names = IlluminaC::PlatePurposes::PLATE_PURPOSE_FLOWS.flatten
-
PlatePurpose.find_all_by_name(names)
-
end
-
1
private :illumina_c_plate_purposes
-
-
1
def freezer
-
Location.find_by_name!('Library creation freezer')
-
end
-
1
private :freezer
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
-
require "#{Rails.root.to_s}/app/models/illumina_c/plate_purposes"
-
-
class Search::FindIlluminaCTubes < Search
-
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
Tube.include_purpose.with_purpose(illumina_c_tube_purposes).with_no_outgoing_transfers.in_state(criteria['state']).without_finished_tubes(illumina_c_final_tube_purpose)
-
end
-
-
def self.illumina_c_tube_purposes
-
Tube::Purpose.find_all_by_name(
-
IlluminaC::PlatePurposes::TUBE_PURPOSE_FLOWS.flatten
-
)
-
end
-
delegate :illumina_c_tube_purposes, :to => 'self.class'
-
-
def self.illumina_c_final_tube_purpose
-
Tube::Purpose.find_all_by_name(
-
IlluminaC::PlatePurposes::TUBE_PURPOSE_FLOWS.map(&:last)
-
)
-
1
end
-
delegate :illumina_c_final_tube_purpose, :to => 'self.class'
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Search::FindLotByBatchId < Search
-
1
def scope(criteria)
-
root_asset = Batch.find_by_id(criteria['batch_id']).try(:parent_of_purpose,'Tag PCR')
-
Lot.with_qc_asset(root_asset)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Search::FindLotByLotNumber < Search
-
1
def scope(criteria)
-
Lot.with_lot_number(criteria['lot_number'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Search::FindModelByName < Search
-
1
validates_presence_of :model_name
-
-
1
def model
-
model_name.constantize
-
end
-
1
private :model
-
-
1
def scope(criteria)
-
model.with_name(criteria['name'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
-
# Pre_PCR plates will remain 'started; until the run is complete.
-
-
class Search::FindOutstandingIlluminaBPrePcrPlates < Search
-
def scope(criteria)
-
Plate.include_plate_metadata.include_plate_purpose.with_plate_purpose(pre_pcr_plate_purpose).in_state(['pending','started'])
-
end
-
-
def self.pre_pcr_plate_purpose
-
PlatePurpose.find_by_name('ILB_STD_PREPCR')
-
1
end
-
delegate :pre_pcr_plate_purpose, :to => 'self.class'
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
1
class Search::FindPulldownPlates < Search
-
1
def scope(criteria)
-
# We find all plates that do not have transfers where they are the source. Once a plate has been transferred (or marked
-
# for transfer) the destination plate becomes the end of the chain.
-
Plate.include_plate_metadata.include_plate_purpose.with_plate_purpose(pulldown_plate_purposes).with_no_outgoing_transfers.in_state(criteria['state'])
-
end
-
-
1
def pulldown_plate_purposes
-
PlatePurpose.find_all_by_name(Pulldown::PlatePurposes::PLATE_PURPOSE_FLOWS.flatten)
-
end
-
1
private :pulldown_plate_purposes
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Search::FindPulldownPlatesForUser < Search::FindPulldownPlates
-
1
def scope(criteria)
-
super.for_user(Uuid.lookup_single_uuid(criteria['user_uuid']).resource)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Search::FindPulldownStockPlates < Search::FindPulldownPlates
-
1
def pulldown_plate_purposes
-
PlatePurpose.find_all_by_name(Pulldown::PlatePurposes::STOCK_PLATE_PURPOSES)
-
end
-
1
private :pulldown_plate_purposes
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Search::FindQcableByBarcode < Search
-
1
def scope(criteria)
-
Qcable.with_machine_barcode(criteria['barcode'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
class Search::FindRobotByBarcode < Search
-
1
def scope(criteria)
-
Robot.with_machine_barcode(criteria['barcode'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Search::FindSourceAssetsByDestinationAssetBarcode < Search
-
1
def scope(criteria)
-
Asset.source_assets_from_machine_barcode(criteria['barcode'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class Search::FindUserByLogin < Search
-
1
def scope(criteria)
-
User.with_login(criteria['login'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class Search::FindUserBySwipecardCode < Search
-
1
def scope(criteria)
-
User.with_swipecard_code(criteria['swipecard_code'])
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.
-
1
module SearchBehaviour
-
-
1
MINIMUM_QUERY_LENGTH = 3
-
-
1
def self.included(base)
-
2
base.helper_method :each_non_empty_search_result
-
end
-
-
1
def search
-
t = Time.now
-
perform_search(params[:q].strip) unless params[:q].blank? || query_invalid?
-
@search_took = Time.now - t
-
@render_start = Time.now
-
respond_to do |format|
-
format.html
-
format.js { render :partial => 'search', :layout => false}
-
end
-
end
-
-
1
private
-
-
1
def perform_search(query)
-
searchable_classes.each do |clazz|
-
instance_variable_set("@#{ clazz.name.underscore.pluralize }", clazz.for_search_query(query,extended).all)
-
end
-
end
-
-
1
def each_non_empty_search_result(&block)
-
searchable_classes.each do |clazz|
-
results = instance_variable_get("@#{ clazz.name.underscore.pluralize }")
-
yield(clazz.name.underscore, results) unless results.blank?
-
end
-
end
-
-
1
def query_invalid?
-
return false if params[:q].length >= MINIMUM_QUERY_LENGTH
-
flash.now[:error] = "Queries should be at least #{MINIMUM_QUERY_LENGTH} characters long"
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2014,2015 Genome Research Ltd.
-
1
class SequencingPipeline < Pipeline
-
1
def sequencing?
-
true
-
end
-
-
1
def request_actions
-
[:remove]
-
end
-
-
1
def inbox_partial
-
group_by_parent? ? 'group_by_parent' : super
-
end
-
-
1
def purpose_information?
-
false
-
end
-
-
1
def is_read_length_consistent_for_batch?(batch)
-
-
if (batch.requests.size == 0) || (batch.requests.first.request_metadata.nil?)
-
# No requests selected or the pipeline doesn't contain metadata to check
-
return true
-
end
-
-
read_length_list = batch.requests.map { |request|
-
request.request_metadata.read_length
-
}.compact
-
-
# The pipeline doen't contain the read_length attribute
-
return true if read_length_list.size == 0
-
-
# There are some requests that don't have the read_length_attribute
-
return false if read_length_list.size != batch.requests.size
-
-
return (read_length_list.uniq.size == 1)
-
end
-
-
# The guys in sequencing want to be able to re-run a request in another batch. What we've agreed is that
-
# the request will be failed and then an identical request will be resubmitted to their inbox. The
-
# "failed" request should not be charged for.
-
1
def detach_request_from_batch(batch, request)
-
request.fail!
-
-
# Note that the request metadata also needs to be cloned for this to work.
-
ActiveRecord::Base.transaction do
-
request.dup.tap do |request_clone|
-
rma = request.request_metadata.attributes.merge(:request=>request_clone)
-
request_clone.update_attributes!(:state => 'pending', :target_asset_id => nil,:request_metadata_attributes => rma)
-
request_clone.comments.create!(:description => "Automatically created clone of request #{request.id} which was removed from Batch #{batch.id} at #{DateTime.now()}")
-
request.comments.create!(:description => "The request #{request_clone.id} is an automatically created clone of this one")
-
end
-
end
-
end
-
-
1
def on_start_batch(batch, user)
-
BroadcastEvent::SequencingStart.create!(:seed=>batch,:user=>user,:properties=>{},:created_at=>DateTime.now)
-
end
-
-
1
def post_release_batch(batch, user)
-
# We call compact to handle ControlRequests which may have no target asset.
-
# In practice this isn't required, as we don't use control lanes any more.
-
# However some old features still use them, and until this behaviour is completely
-
# deprecated we should leave it here.
-
batch.assets.compact.uniq.each(&:index_aliquots)
-
Messenger.create!(:target=>batch,:template=>'FlowcellIO',:root=>'flowcell')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014,2015 Genome Research Ltd.
-
class SequencingRequest < CustomerRequest
-
-
extend Request::AccessioningRequired
-
include Api::Messages::FlowcellIO::LaneExtensions
-
-
has_metadata :as => Request do
-
#redundant with library creation , but THEY are using it .
-
attribute(:fragment_size_required_from, :required =>true, :integer => true)
-
attribute(:fragment_size_required_to, :required =>true, :integer =>true)
-
-
attribute(:read_length, :integer => true, :validator => true, :required => true, :selection =>true )
-
end
-
-
include Request::CustomerResponsibility
-
-
before_validation :clear_cross_projects
-
def clear_cross_projects
-
self.initial_project = nil if submission.try(:cross_project?)
-
self.initial_study = nil if submission.try(:cross_study?)
-
end
-
private :clear_cross_projects
-
-
def create_assets_for_multiplexing
-
barcode = AssetBarcode.new_barcode
-
# Needs a sample?
-
puldown_mx_library = PulldownMultiplexedLibraryTube.create!(:name => "#{barcode}", :barcode => barcode)
-
lane = Lane.create!(:name => puldown_mx_library.name)
-
-
self.update_attributes!(:asset => puldown_mx_library, :target_asset =>lane)
-
end
-
-
1
class RequestOptionsValidator < DelegateValidation::Validator
-
delegate :fragment_size_required_from, :fragment_size_required_to, :to => :target
-
1
validates_numericality_of :fragment_size_required_from, :integer_only => true, :greater_than => 0
-
1
validates_numericality_of :fragment_size_required_to, :integer_only => true, :greater_than => 0
-
end
-
-
1
def order=(_)
-
# Do nothing
-
end
-
-
1
def ready?
-
# It's ready if I don't have any lib creation requests or if all my lib creation requests are closed and
-
# at least one of them is in 'passed' status
-
asset = self.asset
-
return true if asset.nil?
-
requests_as_target = self.asset.requests_as_target
-
return true if requests_as_target.nil?
-
library_creation_requests = requests_as_target.where_is_a? Request::LibraryCreation
-
(library_creation_requests.size==0) || library_creation_requests.all?(&:closed?) && library_creation_requests.any?(&:passed?)
-
end
-
-
1
def self.delegate_validator
-
SequencingRequest::RequestOptionsValidator
-
end
-
-
1
def concentration
-
return " " if lab_events_for_batch(batch).empty?
-
conc = lab_events_for_batch(batch).first.descriptor_value("Concentration")
-
return "#{conc}μl" if conc.present?
-
dna = lab_events_for_batch(batch).first.descriptor_value("DNA Volume")
-
rsb = lab_events_for_batch(batch).first.descriptor_value("RSB Volume")
-
"#{dna}μl DNA in #{rsb}μl RSB"
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class SequenomQcPlate < Plate
-
1
DEFAULT_SIZE = 384
-
1
self.per_page = 50
-
-
1
attr_accessor :gender_check_bypass
-
1
attr_accessor :plate_prefix
-
1
attr_accessor :user_barcode
-
-
1
validates_presence_of :name
-
#validate :source_plates_genders_valid?, :if => :do_gender_checks?
-
#validate :user_barcode_exist?
-
-
1
after_create :populate_wells_from_source_plates
-
#after_create :add_event_to_stock_plates
-
-
1
def print_labels(barcode_printer, number_of_barcodes = 3)
-
BarcodePrinter.print(self.barcode_labels(number_of_barcodes.to_i), barcode_printer.name, prefix, "long", label_text_top, label_text_bottom)
-
end
-
-
1
def source_plates
-
return [] if self.parents.empty?
-
ordered_source_plates = []
-
source_barcodes.each do |plate_barcode|
-
if plate_barcode.blank?
-
ordered_source_plates << nil
-
else
-
ordered_source_plates << self.parents.select{|plate| plate.barcode == plate_barcode}.first
-
end
-
end
-
-
ordered_source_plates
-
end
-
-
1
def default_plate_size
-
DEFAULT_SIZE
-
end
-
-
1
def populate_wells_from_source_plates
-
source_plates.each_with_index do |plate, index|
-
next if plate.nil?
-
copy_source_wells!(plate, index)
-
end
-
end
-
1
handle_asynchronously :populate_wells_from_source_plates
-
-
1
def add_event_to_stock_plates(user_barcode)
-
return false unless user_barcode_exist?(user_barcode)
-
source_plates.each_with_index do |plate, index|
-
next if plate.nil?
-
stock_plate = plate.stock_plate
-
next if stock_plate.nil?
-
stock_plate.events.create_sequenom_stamp!(User.lookup_by_barcode(user_barcode))
-
end
-
self.events.create_sequenom_plate!(User.lookup_by_barcode(user_barcode))
-
end
-
-
1
def compute_and_set_name(input_plate_names)
-
# Only do this if this is a new_record or we'll
-
# be doing it every time we pull it from the db.
-
if new_record?
-
#validate input_plate_names
-
if do_gender_checks? and !source_plates_genders_valid?(input_plate_names)
-
return false
-
end
-
-
return false unless at_least_one_source_plate?(input_plate_names) and input_plates_exist?(input_plate_names)
-
-
# Plate name e.g. QC1234_1235_1236_1237_20100801
-
self.name = "#{plate_prefix}#{plate_number(input_plate_names)}#{plate_date}"
-
self.plate_purpose = PlatePurpose.find_by_name("Sequenom")
-
self.barcode = PlateBarcode.create.barcode
-
connect_input_plates(input_plate_names)
-
end
-
true
-
end
-
1
protected
-
-
1
def source_barcodes
-
[label_match[2], label_match[3], label_match[4], label_match[5]]
-
end
-
-
1
def connect_input_plates(input_plate_names)
-
self.parents = Plate.with_machine_barcode(input_plate_names.values.reject(&:blank?)).all
-
end
-
-
1
def destination_map_based_on_source_row_col_and_quadrant(quadrant, row, col)
-
row_offset, col_offset = quadrant_row_col_offset(quadrant)
-
self.find_map_by_rowcol( (row*2) + row_offset, (col*2) +col_offset )
-
end
-
-
# ---------------------------
-
# | 0,0 | 0,1 |
-
# | Quadrant 0 | Quadrant 1 |
-
# | | |
-
# ---------------------------
-
# | 1,0 | 1,1 |
-
# | Quadrant 2 | Quadrant 3 |
-
# | | |
-
# ---------------------------
-
1
def quadrant_row_col_offset(quadrant)
-
col_offset = case quadrant
-
when 1 then 1
-
when 3 then 1
-
else 0
-
end
-
row_offset = case quadrant
-
when 2 then 1
-
when 3 then 1
-
else 0
-
end
-
-
[row_offset, col_offset]
-
end
-
-
1
def copy_source_well_sequenom_plate!(plate, quadrant, row, col)
-
source_well = plate.find_well_by_rowcol(row, col)
-
return nil if source_well.nil?
-
-
source_well.dup.tap do |cloned_well|
-
cloned_well.plate = self
-
cloned_well.map = destination_map_based_on_source_row_col_and_quadrant(quadrant, row, col)
-
cloned_well.aliquots = source_well.aliquots.map(&:dup)
-
cloned_well.save!
-
-
# FIXME: This fix seems a bit dirty but it works
-
# Adding source_wells directly to cloned_well parents is broken so have to
-
# use the an explict call to AssetLink.connect instead. Unfortunately I
-
# haven't been able to recreate the problem in testing. :(
-
-
# cloned_well.parents << source_well
-
AssetLink.create_edge!(source_well, cloned_well)
-
end
-
end
-
-
-
1
def copy_source_wells!(plate, quadrant)
-
(0..8).each do |row|
-
(0..12).each do |col|
-
copy_source_well_sequenom_plate!(plate, quadrant, row, col)
-
end
-
end
-
end
-
-
1
def input_plates_exist?(input_plate_names)
-
input_plate_names.each do |source_plate_number,source_plate_barcode|
-
next if source_plate_barcode.blank?
-
-
source_plate = Plate.find_from_machine_barcode(source_plate_barcode)
-
-
if source_plate.nil?
-
errors.add(:base,"Source Plate: #{source_plate_barcode} cannot be found")
-
return false
-
end
-
end
-
true
-
end
-
-
1
def at_least_one_source_plate?(input_plate_names)
-
!if input_plate_names.values.select {|v| !v.blank? }.size == 0
-
errors.add(:base,"At least one source input plate barcode must be entered.")
-
end
-
end
-
-
1
def user_barcode_exist?(user_barcode)
-
if User.lookup_by_barcode(user_barcode).nil?
-
errors.add(:base,"Please scan your user barcode") if User.lookup_by_barcode(user_barcode).nil?
-
false
-
else
-
true
-
end
-
end
-
-
-
1
def do_gender_checks?
-
true unless gender_check_bypass
-
end
-
-
# Source plates should exist, obviously, and have contain at least one sample with a gender
-
1
def source_plates_genders_valid?(input_plate_names)
-
input_plate_names.each do |source_plate_number,source_plate_barcode|
-
next if source_plate_barcode.blank?
-
-
source_plate = Plate.find_from_machine_barcode(source_plate_barcode)
-
-
if source_plate.nil?
-
errors.add(:base,"Source Plate: #{source_plate_barcode} cannot be found")
-
return false
-
end
-
# Unless our source plates all contain some samples with gender then
-
# add an error to say things went wrong.
-
unless source_plate.contains_gendered_samples?
-
errors.add(:base,"Failed to create Sequenom QC Plate - Source Plate: #{source_plate_barcode} lacks gender information")
-
return false
-
# errors.add(input_plate_names[source_plate_number], "Source Plate: #{source_plate_barcode} lacks gender information")
-
end
-
end
-
true
-
end
-
-
1
def barcode_labels(number_of_barcodes)
-
(1..number_of_barcodes).map do |plate_number|
-
PrintBarcode::Label.new(:number => self.barcode, :prefix => prefix, :suffix => plate_purpose.name)
-
end
-
end
-
-
# Create a match object for the input plate names from this
-
# sequenom plate's name.
-
1
def label_match
-
@label_match ||= name.match(/^([^\d]+)(\d+)?_(\d+)?_(\d+)?_(\d+)?_(\d+)$/)
-
end
-
-
1
def label_text_top
-
"#{plate_label(2)} #{plate_label(3)}"
-
end
-
-
1
def label_text_bottom
-
"#{plate_label(4)} #{plate_label(5)}"
-
end
-
-
# This is the date format used by show when the plate was created
-
# E.g. 1st August 2010 => 20100801.
-
1
def plate_date
-
Time.now.strftime("%Y%m%d")
-
end
-
-
# Return the matching plates human barcode padded out to 8 charactors
-
# or just 8 space charactors if there's no input plate that position.
-
1
def plate_label(plate_number)
-
(label_match[plate_number] || "").ljust(7,"\s")
-
end
-
-
# Should join the values of the input_plate_names hash using underscores,
-
# to give a new plate name.
-
#
-
# The order is (plates are interlieved):
-
#
-
# _______________________
-
# | | |
-
# | Plate 1 | Plate 2 |
-
# | A1 | A1 |
-
# |-----------|-----------|
-
# | | |
-
# | Plate 3 | Plate 4 |
-
# | A1 | A1 |
-
# _______________________
-
-
# ...to give "plate1_plate2_plate3_plate4"
-
1
def plate_number(input_plate_names)
-
input_plate_names.inject("") do |return_value, (index, barcode)|
-
human_plate_name = Barcode.number_to_human(barcode) || ""
-
return_value << human_plate_name << "_"
-
end
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
1
class SetCharacterisationDescriptorsTask < Task
-
1
def partial
-
"set_characterisation_descriptors"
-
end
-
-
1
def render_task(workflows_controller, params)
-
super
-
workflows_controller.render_set_characterisation_descriptors_task(self, params)
-
end
-
-
-
1
def do_task(workflows_controller, params)
-
workflows_controller.do_set_characterisation_descriptors_task(self,params)
-
end
-
-
1
def sub_events_for_event(event)
-
return [] unless event.eventful.respond_to?(:asset)
-
subassets = subassets_for_asset(event.eventful.asset).select do |asset|
-
# we don't want anything except fragment gel so far ...
-
asset.is_a?(Fragment) && self.name == "Gel"
-
end
-
return subassets.map { |a| generate_events_from_descriptors(a) }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class SetDescriptorsTask < Task
-
-
1
def render_task(workflows_controller, params)
-
super
-
workflows_controller.render_set_descriptors_task(self, params)
-
end
-
-
-
1
def do_task(workflows_controller, params)
-
workflows_controller.do_set_descriptors_task(self,params)
-
end
-
-
1
def sub_events_for_event(event)
-
return [] unless event.eventful.respond_to?(:asset)
-
subassets = subassets_for_asset(event.eventful.asset).select do |asset|
-
# we don't want anything except fragment gel so far ...
-
asset.is_a?(Fragment) && self.name == "Gel"
-
end
-
return subassets.map { |a| generate_events_from_descriptors(a) }
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class SetLocationTask < Task
-
-
1
set_subclass_attribute :acts_on_input, :kind => :bool, :default => false, :display_name => "Set location of input assets if ticked (output otherwise)"
-
1
set_subclass_attribute :location_id, :cast => :int, :default => 4, :kind => :selection, :display_name => "Choose default location", :choices => -> { Location.all.map{ |l| [l.name, l.id]}}
-
-
1
def partial
-
"set_location"
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_set_location_task(self, params)
-
end
-
-
1
def set_location(asset, location_id)
-
asset = Asset.find(asset) unless asset.is_a? Asset
-
asset.location_id = location_id
-
asset.save
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Setting < ActiveRecord::Base
-
1
belongs_to :user
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
module SharedBehaviour::Deprecatable
-
-
1
def self.included(base)
-
2
base.class_eval do
-
2
scope :active, ->() { where(:deprecated_at => nil) }
-
end
-
end
-
-
1
def deprecate!
-
self.deprecated_at = DateTime.now
-
save!
-
end
-
-
# If we have a datestamp we are deprecated
-
1
def deprecated?
-
deprecated_at?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
module SharedBehaviour::Immutable
-
-
1
MUTABLE = ['deprecated_at']
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
before_update :save_allowed?
-
end
-
end
-
-
1
private
-
-
1
def save_allowed?
-
return true if (self.changed - MUTABLE).empty?
-
raise ActiveRecord::RecordNotSaved, 'This record is immutable. Deprecate it and create a replacement instead.'
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
1
module SharedBehaviour::Indestructable
-
-
1
def self.included(base)
-
2
base.class_eval do
-
2
before_destroy :prevent_destruction
-
end
-
end
-
-
1
private
-
-
1
def prevent_destruction
-
errors.add(:base,'can not be destroyed and should be deprecated instead!')
-
raise ActiveRecord::RecordNotDestroyed, self
-
false
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module SharedBehaviour::Named
-
1
def self.included(base)
-
4
base.class_eval do
-
4
scope :with_name, ->(*names) { where(:name => names.flatten) }
-
4
scope :sorted_by_name, -> { order('name ASC') }
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class SingleRequestSubmission < Order
-
1
def request_type_id=(request_type_id)
-
request_type_ids_list = [[request_type_id]]
-
end
-
-
1
def request_type_id
-
request_type_ids_list.first.first
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014 Genome Research Ltd.
-
1
class SpecificTubeCreation < TubeCreation
-
1
class ChildPurpose < ActiveRecord::Base
-
1
self.table_name =('specific_tube_creation_purposes')
-
1
belongs_to :specific_tube_creation
-
1
belongs_to :tube_purpose, :class_name => 'Purpose'
-
end
-
-
1
has_many :creation_child_purposes, :class_name => 'SpecificTubeCreation::ChildPurpose'
-
1
has_many :child_purposes, :through => :creation_child_purposes, :source => :tube_purpose
-
-
1
validates_presence_of :child_purposes
-
-
1
def set_child_purposes=(uuids)
-
self.child_purposes = uuids.map {|uuid| Uuid.find_by_external_id(uuid).resource }
-
end
-
-
1
def no_pooling_expected?
-
true
-
end
-
1
private :no_pooling_expected?
-
-
1
def create_children!
-
self.children = (child_purposes).map { |child_purpose| child_purpose.create! }
-
end
-
1
private :create_children!
-
-
1
def multiple_purposes
-
true
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class SpikedBuffer < LibraryTube
-
# The index of a spiked buffer is the first parent library tube. Note that this does not cover cases where
-
# the sti_type is a derivative of LibraryTube, which is actually fine because SpikedBuffer is a LibraryTube
-
# and we definitely don't want that in the list.
-
1
has_one_as_child(:index, :conditions => { :sti_type => 'LibraryTube' },:order => 'id DESC')
-
-
1
def library_prep?
-
false
-
end
-
-
# Before the validations are run on creation we need to ensure that there is at least an aliquot of phiX
-
# in this tube.
-
1
before_validation(:on => :create) do |record|
-
record.aliquots.build(:sample => record.class.phiX_sample) if record.aliquots.empty?
-
end
-
-
1
def self.phiX_sample
-
Sample.find_by_name('phiX_for_spiked_buffers') or raise StandardError, "Cannot find phiX_for_spiked_buffers sample"
-
end
-
-
1
def percentage_of_index
-
return nil unless index
-
100*index.volume/volume
-
end
-
-
1
def transfer(transfer_volume)
-
-
index_volume_to_transfer = index.volume*transfer_volume.to_f/self.volume # to do before super which modifies self.volume
-
super(transfer_volume).tap do |new_asset|
-
new_asset.index = index.transfer(index_volume_to_transfer)
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
##
-
# A stamp is a means of transfering material from a lot
-
# into a qcable.
-
-
1
class Stamp < ActiveRecord::Base
-
-
1
include Uuid::Uuidable
-
1
include ModelExtensions::Stamp
-
-
1
class StampQcable < ActiveRecord::Base
-
-
1
self.table_name =('stamp_qcables')
-
-
1
belongs_to :stamp, :inverse_of => :stamp_qcables
-
1
belongs_to :qcable, :inverse_of => :stamp_qcable
-
1
validates :stamp, :presence => true
-
1
validates :qcable, :presence => true
-
1
validates :bed, :presence => true
-
1
validates :order, :presence => true
-
-
end
-
-
1
belongs_to :lot
-
1
belongs_to :robot
-
1
belongs_to :user
-
-
1
has_many :qcables, :through => :stamp_qcables
-
1
has_many :stamp_qcables, :inverse_of => :stamp, :class_name => 'Stamp::StampQcable'
-
-
1
validates :lot, :presence => true
-
1
validates :user, :presence => true
-
1
validates :robot, :presence => true
-
1
validates :tip_lot, :presence => true
-
-
1
after_create :stamp!
-
-
1
private
-
1
def stamp!
-
ActiveRecord::Base.transaction do
-
qcables.each(&:do_stamp!)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module StandardNamedScopes
-
1
def self.included(base)
-
4
base.instance_eval do
-
# Date ordering is better specified as "order_most_recently_created_first" or
-
# "order_most_recently_updated_last". These names seem more readable and understandable.
-
4
[ :created, :updated ].each do |field|
-
8
{ :first => 'DESC', :last => 'ASC' }.each do |position, order_by|
-
16
scope :"order_most_recently_#{field}_#{position}", -> { order("#{self.quoted_table_name}.#{field}_at #{order_by}") }
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
# Performs a change of state on an asset.
-
#
-
#--
-
# This code assumes that there is no statemachine on the requests. Whilst this is not true it will be in the
-
# future and we are safe to assume that, in the case of Pulldown, this is ok. The state of a plate, or the MX
-
# library tube, in Pulldown is considered to be the state of the TransferRequests leading into it. The state
-
# machine for the asset is defined within the client application, hence the statemachine on the requests will
-
# be removed from the core of sequencescape at some point.
-
#++
-
1
class StateChange < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
# This is the target asset for which to update the state
-
1
belongs_to :target, :class_name => 'Asset'
-
1
validates_presence_of :target
-
-
# If the state change is a known failure state then a reason must be included
-
1
validates :reason, :presence => true, :if => :targetted_for_failure?
-
-
1
def targetted_for_failure?
-
[ 'failed', 'cancelled' ].include?(target_state)
-
end
-
1
private :targetted_for_failure?
-
-
1
include Asset::Ownership::ChangesOwner
-
1
set_target_for_owner(:target)
-
-
# Some targets can have "contents" updated (notably plates). The meaning of this is is dealt with by the
-
# target being updated.
-
1
serialize :contents
-
-
1
attr_accessor :customer_accepts_responsibility
-
-
# These track the state of the target. The target_state is what we want it to end up in and the previous_state
-
# records the state that it was in before the update. The previous_state is not assigned by the creator but
-
# by the action of making the transition.
-
1
validates_presence_of :target_state
-
1
validates_unassigned(:previous_state)
-
-
# Before creating an instance we record the current state of the target.
-
1
before_create :record_current_state_of_target
-
1
def record_current_state_of_target
-
self.previous_state = target.state
-
end
-
1
private :record_current_state_of_target
-
-
# After creation update the state of the target asset, leaving it to do the right thing.
-
# After state change, update the owner
-
1
after_create :update_state_of_target
-
1
def update_state_of_target
-
target.transition_to(target_state, user, contents, customer_accepts_responsibility)
-
end
-
1
private :update_state_of_target
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class StockLibraryTube < Tube
-
1
include Asset::Stock
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class StockMultiplexedLibraryTube < Tube
-
1
include Asset::Stock
-
-
1
def stock_wells
-
purpose.stock_wells(self)
-
end
-
-
1
def created_with_request_options
-
parent.created_with_request_options
-
end
-
-
1
def sibling_tubes
-
purpose.sibling_tubes(self)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class StockSampleTube < Tube
-
1
include Asset::Stock
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class StripCreationRequest < CustomerRequest
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
##
-
# StripTubes can be thought of as long skinny plates.
-
# Unlike normal plates they can be kept in a rack.
-
# Strip tubes don't get a barcode assigned upfront.
-
1
class StripTube < Plate
-
1
contained_by :asset_rack
-
-
1
self.prefix = 'LS'
-
-
1
def friendly_name
-
name
-
end
-
-
1
def subject_type
-
'strip_tube'
-
end
-
-
1
def library_source_plates
-
source_plates
-
end
-
-
# Until we no how barcodes are going to work, we'll just override this
-
1
def self.create_with_barcode!(*args, &block)
-
attributes = args.extract_options!
-
barcode = args.first || attributes[:barcode]
-
create!(attributes.merge(:barcode => barcode), &block)
-
end
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class StripTubeCreationPipeline < Pipeline
-
-
1
INBOX_PARTIAL = 'group_by_parent'
-
-
1
def inbox_partial
-
INBOX_PARTIAL
-
end
-
-
1
def purpose_information?
-
false
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
class StripTubeCreationTask < Task
-
-
1
belongs_to :purpose
-
-
1
def render_task(workflow, params)
-
workflow.render_strip_tube_creation_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_strip_tube_creation_task(self, params)
-
end
-
-
1
def partial
-
self.class.to_s.underscore.chomp('_task')
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
-
1
require 'aasm'
-
-
1
class Study < ActiveRecord::Base
-
-
1
include StudyReport::StudyDetails
-
1
include ModelExtensions::Study
-
-
1
include Api::StudyIO::Extensions
-
-
1
self.per_page = 500
-
1
include Uuid::Uuidable
-
-
1
include EventfulRecord
-
1
include AASM
-
1
include DataRelease
-
1
include Commentable
-
1
include Identifiable
-
1
include SharedBehaviour::Named
-
1
include ReferenceGenome::Associations
-
1
include SampleManifest::Associations
-
1
include Request::Statistics::DeprecatedMethods
-
-
1
extend EventfulRecord
-
1
has_many_events
-
1
has_many_lab_events
-
-
1
acts_as_authorizable
-
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
1
aasm_state :pending
-
1
aasm_state :active, :enter => :mark_active
-
1
aasm_state :inactive, :enter => :mark_deactive
-
-
1
aasm_event :reset do
-
1
transitions :to => :pending, :from => [:inactive, :active]
-
end
-
-
1
aasm_event :activate do
-
1
transitions :to => :active, :from => [:pending, :inactive]
-
end
-
-
1
aasm_event :deactivate do
-
1
transitions :to => :inactive, :from => [:pending, :active]
-
end
-
-
1
attr_accessor :approval
-
1
attr_accessor :run_count
-
1
attr_accessor :total_price
-
-
1
include Role::Authorized
-
1
role_relation(:followed_by, 'follower')
-
1
role_relation(:managed_by, 'manager')
-
1
role_relation(:collaborated_with, 'collaborator')
-
-
1
has_many_users_through_roles(:owners)
-
1
has_many_users_through_roles(:managers)
-
1
has_many_users_through_roles(:followers)
-
1
has_many_users_through_roles(:"Data Access Contacts")
-
-
1
belongs_to :user
-
-
1
has_many :study_samples #, :group => 'study_id, sample_id', :conditions => 'sample_id IS NOT NULL'
-
1
has_many :orders
-
1
has_many :submissions, :through => :orders
-
1
has_many :samples, :through => :study_samples
-
1
has_many :batches
-
-
# requests read only so no need to use has_many
-
# this return a proper namescope which can be nicely chained
-
1
def requests(reload=nil)
-
Request.for_study(self)
-
end
-
-
1
has_many :asset_groups
-
1
has_many :study_reports
-
-
#load all the associated requests with attemps and request type
-
1
has_many :eager_items , :class_name => "Item", :include => [{:requests => [:request_type]}], :through => :requests, :source => :item # , :uniq => true
-
-
1
has_many :aliquots
-
1
has_many :assets_through_aliquots, :class_name => "Asset", :through => :aliquots, :source => :receptacle, :uniq => :true
-
1
has_many :assets_through_requests, :class_name => "Asset", :through => :initial_requests, :source => :asset, :uniq => :true
-
-
1
has_many :items , :through => :requests, :uniq => true
-
-
# This is a performance enhancement! Projects and studies are related through
-
# requests but the requests table can be huge, and then performance can really
-
# suck (200s load times for 100 studies when eager loading projects). Instead
-
# this reduces the requests for this study down to unique projects, and then
-
# says use those unique projects. That knocks times down to 0.15s for the same
-
# studies!
-
#has_many :project_requests, :class_name => 'Request', :group => 'study_id, project_id', :conditions => 'project_id IS NOT NULL'
-
#has_many :projects, :class_name => "Project", :through => :project_requests, :source => :initial_project, :uniq => true
-
-
#New version
-
1
has_many :projects,:through => :orders, :uniq => true
-
-
1
has_many :initial_requests, :class_name => "Request", :foreign_key => :initial_study_id
-
-
1
has_many :comments, :as => :commentable
-
1
has_many :events, :as => :eventful, :order => 'created_at ASC, id ASC'
-
1
has_many :documents, :as => :documentable
-
1
has_many :sample_manifests
-
1
has_many :suppliers, :through => :sample_manifests, :uniq => true
-
-
1
include StudyRelation::Associations
-
-
1
squishify :name
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :on => :create, :message => "already in use (#{self.name})"
-
1
validates_length_of :name, :maximum => 200
-
1
validates_format_of :abbreviation, :with => /^[\w_-]+$/i, :allow_blank => false, :message => 'cannot contain spaces or be blank'
-
-
1
validate :validate_ethically_approved
-
1
def validate_ethically_approved
-
1
return true if valid_ethically_approved?
-
message = self.ethical_approval_required? ? "should be either true or false for this study." : "should be not applicable (null) not false."
-
errors.add(:ethically_approved, message)
-
false
-
end
-
-
1
def valid_ethically_approved?
-
1
self.ethical_approval_required? ? !ethically_approved.nil? : ethically_approved != false
-
end
-
1
private :valid_ethically_approved?
-
-
1
before_validation :set_default_ethical_approval
-
1
def set_default_ethical_approval
-
1
self.ethically_approved ||= self.ethical_approval_required? ? false : nil
-
true
-
1
end
-
1
private :set_default_ethical_approval
-
-
1
scope :for_search_query, ->(query,with_includes) {
-
joins(:study_metadata).where([ 'name LIKE ? OR studies.id=? OR prelim_id=?', "%#{query}%", query, query ])
-
}
-
-
1
scope :with_no_ethical_approval, -> { where( :ethically_approved => false ) }
-
-
1
scope :is_active, -> { where( :state => 'active' ) }
-
1
scope :is_inactive, -> { where( :state => 'inactive' ) }
-
1
scope :is_pending, -> { where( :state => 'pending' ) }
-
-
1
scope :newest_first, -> { order("#{ self.quoted_table_name }.created_at DESC" ) }
-
1
scope :with_user_included, -> { includes(:user) }
-
-
1
scope :in_assets, ->(assets) {
-
select('DISTINCT studies.*').
-
joins([
-
'LEFT JOIN aliquots ON aliquots.study_id = studies.id',
-
]).
-
where(['aliquots.receptacle_id IN (?)',assets.map(&:id)])
-
}
-
-
1
scope :alphabetical, ->() { order('name ASC') }
-
1
scope :for_listing, ->() { select('name, id') }
-
-
1
def each_well_for_qc_report_in_batches(exclude_existing,product_criteria)
-
base_scope = Well.on_plate_purpose(PlatePurpose.find_all_by_name(['Stock Plate','Stock RNA Plate'])).
-
for_study_through_aliquot(self).
-
without_blank_samples.
-
includes(:well_attribute, samples: :sample_metadata ).
-
readonly(true)
-
scope = exclude_existing ? base_scope.without_report(product_criteria) : base_scope
-
scope.find_in_batches {|wells| yield wells }
-
end
-
-
1
YES = 'Yes'
-
1
NO = 'No'
-
1
YES_OR_NO = [ YES, NO ]
-
1
Other_type = "Other"
-
-
1
STUDY_SRA_HOLDS = [ 'Hold', 'Public' ]
-
-
1
DATA_RELEASE_STRATEGY_OPEN = 'open'
-
1
DATA_RELEASE_STRATEGY_MANAGED = 'managed'
-
1
DATA_RELEASE_STRATEGY_NOT_APPLICABLE = 'not applicable'
-
1
DATA_RELEASE_STRATEGIES = [ DATA_RELEASE_STRATEGY_OPEN, DATA_RELEASE_STRATEGY_MANAGED, DATA_RELEASE_STRATEGY_NOT_APPLICABLE ]
-
-
1
DATA_RELEASE_TIMING_STANDARD = 'standard'
-
1
DATA_RELEASE_TIMING_NEVER = 'never'
-
1
DATA_RELEASE_TIMING_DELAYED = 'delayed'
-
1
DATA_RELEASE_TIMINGS = [
-
DATA_RELEASE_TIMING_STANDARD,
-
'immediate',
-
DATA_RELEASE_TIMING_DELAYED
-
]
-
1
DATA_RELEASE_PREVENTION_REASONS = [
-
'data validity',
-
'legal',
-
'replication of data subset'
-
]
-
-
1
DATA_RELEASE_DELAY_FOR_OTHER = 'other'
-
1
DATA_RELEASE_DELAY_REASONS_STANDARD = [
-
'phd study',
-
DATA_RELEASE_DELAY_FOR_OTHER
-
]
-
1
DATA_RELEASE_DELAY_REASONS_ASSAY = [
-
'phd study',
-
'assay of no other use',
-
DATA_RELEASE_DELAY_FOR_OTHER
-
]
-
-
1
DATA_RELEASE_DELAY_LONG = [ '6 months', '9 months', '12 months' ]
-
1
DATA_RELEASE_DELAY_SHORT = [ '3 months' ]
-
1
DATA_RELEASE_DELAY_PERIODS = DATA_RELEASE_DELAY_SHORT + DATA_RELEASE_DELAY_LONG
-
-
1
extend Metadata
-
1
has_metadata do
-
1
include StudyType::Associations
-
1
include DataReleaseStudyType::Associations
-
1
include ReferenceGenome::Associations
-
1
include FacultySponsor::Associations
-
-
1
association(:study_type, :name, :required => true)
-
1
association(:data_release_study_type, :name, :required => true)
-
1
association(:reference_genome, :name, :required => true)
-
1
association(:faculty_sponsor, :name, :required => true)
-
-
1
attribute(:prelim_id, :with => /^[a-zA-Z]\d{4}$/, :required => false)
-
1
attribute(:study_description, :required => true)
-
1
attribute(:contaminated_human_dna, :required => true, :in => YES_OR_NO)
-
1
attribute(:remove_x_and_autosomes, :required => true, :default => 'No', :in => YES_OR_NO)
-
1
attribute(:separate_y_chromosome_data, :required => true, :default=> false, :boolean => true)
-
1
attribute(:study_project_id)
-
1
attribute(:study_abstract)
-
1
attribute(:study_study_title)
-
1
attribute(:study_ebi_accession_number)
-
1
attribute(:study_sra_hold, :required => true, :default => 'Hold', :in => STUDY_SRA_HOLDS)
-
1
attribute(:contains_human_dna, :required => true, :in => YES_OR_NO)
-
1
attribute(:commercially_available, :required => true, :in => YES_OR_NO)
-
1
attribute(:study_name_abbreviation)
-
-
1
attribute(:data_release_strategy, :required => true, :in => DATA_RELEASE_STRATEGIES, :default => DATA_RELEASE_STRATEGY_MANAGED)
-
1
attribute(:data_release_standard_agreement, :default => YES, :in => YES_OR_NO, :if => :managed?)
-
-
1
attribute(:data_release_timing, :required => true, :default => DATA_RELEASE_TIMING_STANDARD, :in => DATA_RELEASE_TIMINGS + [ DATA_RELEASE_TIMING_NEVER ])
-
1
attribute(:data_release_delay_reason, :required => true, :in => DATA_RELEASE_DELAY_REASONS_ASSAY, :if => :delayed_release?)
-
1
attribute(:data_release_delay_period, :required => true, :in => DATA_RELEASE_DELAY_PERIODS, :if => :delayed_release?)
-
1
attribute(:bam, :default => true)
-
-
1
with_options(:required => true, :if => :delayed_for_other_reasons?) do |required|
-
1
required.attribute(:data_release_delay_other_comment)
-
1
required.attribute(:data_release_delay_reason_comment)
-
end
-
-
1
attribute(:dac_policy, :default => configatron.default_policy_text, :if => :managed?)
-
1
attribute(:dac_policy_title, :default => configatron.default_policy_title, :if => :managed?)
-
1
attribute(:ega_dac_accession_number)
-
1
attribute(:ega_policy_accession_number)
-
1
attribute(:array_express_accession_number)
-
-
1
with_options(:if => :delayed_for_long_time?, :required => true) do |required|
-
1
required.attribute(:data_release_delay_approval, :in => YES_OR_NO, :default => NO)
-
end
-
-
1
with_options(:if => :never_release?, :required => true) do |required|
-
1
required.attribute(:data_release_prevention_reason, :in => DATA_RELEASE_PREVENTION_REASONS)
-
1
required.attribute(:data_release_prevention_approval, :in => YES_OR_NO)
-
1
required.attribute(:data_release_prevention_reason_comment)
-
end
-
-
1
attribute(:data_access_group, :with=> /\A[a-z_][a-z0-9_-]{0,31}(?:\s+[a-z_][a-z0-9_-]{0,31})*\Z/)
-
-
# SNP information
-
1
attribute(:snp_study_id, :integer => true)
-
1
attribute(:snp_parent_study_id, :integer => true)
-
-
1
attribute(:number_of_gigabases_per_sample)
-
-
1
attribute(:hmdmc_approval_number)
-
-
1
REMAPPED_ATTRIBUTES = {
-
:contaminated_human_dna => YES_OR_NO,
-
:remove_x_and_autosomes => YES_OR_NO,
-
:study_sra_hold => STUDY_SRA_HOLDS,
-
:contains_human_dna => YES_OR_NO,
-
:commercially_available => YES_OR_NO
-
}.inject({}) do |h,(k,v)|
-
15
h[k] = v.inject({}) { |a,b| a[b.downcase] = b ; a }
-
5
h
-
end
-
-
1
before_validation do |record|
-
2
record.reference_genome_id = 1 if record.reference_genome_id.blank?
-
-
# Unfortunately it appears that some of the functionality of this implementation relies on non-capitalisation!
-
# So we remap the lowercased versions to their proper values here
-
2
REMAPPED_ATTRIBUTES.each do |attribute,mapping|
-
10
record[attribute] = mapping.fetch(record[attribute].try(:downcase), record[attribute])
-
10
record[attribute] = nil if record[attribute].blank? # Empty strings should be nil
-
end
-
end
-
-
end
-
-
1
class Metadata
-
-
1
def remove_x_and_autosomes?
-
remove_x_and_autosomes == YES
-
end
-
-
1
def managed?
-
8
self.data_release_strategy == DATA_RELEASE_STRATEGY_MANAGED
-
end
-
-
1
def delayed_release?
-
12
self.data_release_timing == DATA_RELEASE_TIMING_DELAYED
-
end
-
-
1
def never_release?
-
16
self.data_release_timing == DATA_RELEASE_TIMING_NEVER
-
end
-
-
1
def delayed_for_other_reasons?
-
8
self.data_release_delay_reason == DATA_RELEASE_DELAY_FOR_OTHER
-
end
-
-
1
def delayed_for_long_time?
-
6
DATA_RELEASE_DELAY_PERIODS.include?(self.data_release_delay_period)
-
end
-
-
1
validates_numericality_of :number_of_gigabases_per_sample, :greater_than_or_equal_to => 0.15, :allow_blank => true, :allow_nil => true
-
-
1
has_one :data_release_non_standard_agreement, :class_name => 'Document', :as => :documentable
-
1
accepts_nested_attributes_for :data_release_non_standard_agreement
-
1
validates :data_release_non_standard_agreement, :presence => true, :if => :non_standard_agreement?
-
1
validates_associated :data_release_non_standard_agreement, :if => :non_standard_agreement?
-
-
1
validate :valid_policy_url?
-
-
1
validate :sanity_check_y_separation, :if => :separate_y_chromosome_data?
-
-
1
def sanity_check_y_separation
-
self.errors.add(:separate_y_chromosome_data,'cannot be selected with remove x and autosomes.') if remove_x_and_autosomes?
-
!remove_x_and_autosomes?
-
end
-
-
-
1
before_validation do |record|
-
2
if not record.non_standard_agreement? and not record.data_release_non_standard_agreement.nil?
-
record.data_release_non_standard_agreement.delete
-
record.data_release_non_standard_agreement = nil
-
end
-
end
-
-
1
def non_standard_agreement?
-
6
self.data_release_standard_agreement == NO
-
end
-
-
1
def study_type_valid?
-
self.errors.add(:study_type, "is not specified") if study_type.name == "Not specified"
-
end
-
-
1
def valid_policy_url?
-
# Rails 2.3 has no inbuilt URL validation, but rather than rolling our own, we'll
-
# use the inbuilt ruby URI parser, a bit like here:
-
# http://www.simonecarletti.com/blog/2009/04/validating-the-format-of-an-url-with-rails/
-
2
return true if dac_policy.blank?
-
dac_policy.insert(0,"http://") if /:\/\//.match(dac_policy).nil? # Add an http protocol if no protocol is defined
-
begin
-
uri = URI.parse(dac_policy)
-
raise URI::InvalidURIError if configatron.invalid_policy_url_domains.include?(uri.host)
-
rescue URI::InvalidURIError
-
self.errors.add(:dac_policy, ": #{dac_policy} is not a valid URL")
-
return false
-
end
-
return true
-
end
-
-
1
with_options(:if => :validating_ena_required_fields?) do |ena_required_fields|
-
1
ena_required_fields.validates_presence_of :data_release_strategy
-
1
ena_required_fields.validates_presence_of :data_release_timing
-
1
ena_required_fields.validates_presence_of :study_description
-
1
ena_required_fields.validates_presence_of :study_abstract
-
1
ena_required_fields.validates_presence_of :study_study_title
-
1
ena_required_fields.validate :study_type_valid?
-
end
-
-
1
def snp_parent_study
-
return nil if self.snp_parent_study_id.nil?
-
self.class.first(:conditions => { :snp_study_id => self.snp_parent_study_id }, :include => :study).try(:study)
-
end
-
-
1
def snp_child_studies
-
return nil if self.snp_study_id.nil?
-
self.class.all(:conditions => { :snp_parent_study_id => self.snp_study_id }, :include => :study).map(&:study)
-
end
-
end
-
-
# We only need to validate the field if we are enforcing data release
-
1
def validating_ena_required_fields_with_enforce_data_release=(state)
-
self.validating_ena_required_fields_without_enforce_data_release = state if self.enforce_data_release
-
end
-
1
alias_method_chain(:validating_ena_required_fields=, :enforce_data_release)
-
-
1
def warnings
-
if study_metadata.managed? && study_metadata.data_access_group.blank?
-
"No user group specified for a managed study. Please specify a valid Unix user group to ensure study data is visible to the correct people."
-
end
-
end
-
-
1
def mark_deactive
-
unless self.inactive?
-
logger.warn "Study deactivation failed! #{self.errors.map{|e| e.to_s} }"
-
end
-
end
-
-
1
def mark_active
-
unless self.active?
-
logger.warn "Study activation failed! #{self.errors.map{|e| e.to_s} }"
-
end
-
end
-
-
1
def completed(workflow=nil)
-
rts = workflow.present? ? workflow.request_types.map(&:id) : RequestType.all.map(&:id)
-
total = self.requests.request_type(rts).count
-
failed = self.requests.failed.request_type(rts).count
-
cancelled = self.requests.cancelled.request_type(rts).count
-
if (total - failed - cancelled) > 0
-
completed_percent = ((self.requests.passed.request_type(rts).count.to_f / (total - failed - cancelled).to_f)*100)
-
completed_percent.to_i
-
else
-
return 0
-
end
-
end
-
-
1
def submissions_for_workflow(workflow)
-
orders.for_workflow(workflow).include_for_study_view.map(&:submission).compact.uniq
-
end
-
-
# Yields information on the state of all request types in a convenient fashion for displaying in a table.
-
1
def request_progress(&block)
-
yield(self.initial_requests.progress_statistics)
-
end
-
-
# Yields information on the state of all assets in a convenient fashion for displaying in a table.
-
1
def asset_progress(assets = nil, &block)
-
conditions = { }
-
conditions[:conditions] = "asset_id IN (#{assets.map(&:id).join(',')})" unless assets.blank?
-
yield(self.initial_requests.asset_statistics(conditions))
-
end
-
-
# Yields information on the state of all samples in a convenient fashion for displaying in a table.
-
1
def sample_progress(samples = nil, &block)
-
conditions = { }
-
conditions[:conditions] = ["sample_id IN (#{samples.map(&:id).join(',')})"] unless samples.blank?
-
yield(self.requests.sample_statistics(conditions))
-
end
-
-
1
def study_status
-
self.inactive? ? "closed" : "open"
-
end
-
-
1
def dac_refname
-
"DAC for study - #{name} - ##{self.id}"
-
end
-
-
1
def unprocessed_submissions?
-
#TODO[mb14] optimize if needed
-
study.orders.any? { |o| o.submission.nil? || o.submission.unprocessed?}
-
end
-
### TODO - Everything below should be treated as legacy until cleaned
-
-
# Used by EventfulMailer
-
1
def study
-
self
-
end
-
-
# Checks if a user is following THIS study
-
1
def followed_by?(user)
-
user.following? self
-
end
-
-
# Returns the study owner (user) if exists or nil
-
# TODO - Should be "owners" and return all owners or empty array - done
-
# TODO - Look into this is the person that created it really the owner?
-
# If so, then an owner should be created when a study is created.
-
-
1
def owner
-
owners.first
-
end
-
-
1
def locale
-
self.funding_source
-
end
-
-
1
def total_requested(ended)
-
count = 0
-
ended_items(ended).each do |item|
-
count = count + item.total_requested
-
end
-
count
-
end
-
-
-
1
def ended_items(ended)
-
i = []
-
self.samples.each do |sample|
-
if sample.matches_end? ended
-
sample.items.each do |item|
-
i.push item
-
end
-
end
-
end
-
i
-
end
-
-
1
def add_reference(params = {})
-
#if the assembly is a curated one from titan
-
if params[:assembly][:reference]
-
@reference = Sequence.find(params[:assembly][:reference])
-
self.add_reference_descriptors(@reference)
-
return true
-
#if it isn't (i.e. instead it's a file uploaded by the user)
-
elsif params[:assembly][:kind] == "None"
-
return true
-
elsif params[:assembly][:kind] == ""
-
return true
-
else
-
@sequence = Sequence.new(params[:assembly])
-
@sequence.name = self.id.to_s
-
@sequence.set_prefix(self.class.to_s.downcase)
-
if @sequence.save
-
self.add_reference_descriptors(@sequence)
-
else
-
return false
-
end
-
end
-
end
-
-
1
scope :awaiting_ethical_approval,
-
joins(:study_metadata).
-
where(
-
:ethically_approved => false,
-
:study_metadata => {
-
:contains_human_dna => Study::YES,
-
:contaminated_human_dna => Study::NO,
-
:commercially_available => Study::NO
-
}
-
)
-
-
-
1
scope :contaminated_with_human_dna,
-
joins(:study_metadata).
-
where(
-
:study_metadata => {
-
:contaminated_human_dna => Study::YES
-
}
-
)
-
-
1
scope :with_remove_x_and_autosomes,
-
joins(:study_metadata).
-
where(
-
:study_metadata => {
-
:remove_x_and_autosomes => Study::YES
-
}
-
)
-
-
-
1
def ebi_accession_number
-
self.study_metadata.study_ebi_accession_number
-
end
-
-
1
def dac_accession_number
-
self.study_metadata.ega_dac_accession_number
-
end
-
-
1
def policy_accession_number
-
self.study_metadata.ega_policy_accession_number
-
end
-
-
1
def accession_number?
-
not ebi_accession_number.blank?
-
end
-
-
1
def data_release_strategy
-
self.study_metadata.data_release_strategy
-
end
-
-
1
def abbreviation
-
1
abbreviation = self.study_metadata.study_name_abbreviation
-
1
abbreviation.blank? ? "#{self.id}STDY" : abbreviation
-
end
-
-
1
def dehumanise_abbreviated_name
-
self.abbreviation.downcase.gsub(/ +/,'_')
-
end
-
-
1
def approved?
-
#TODO remove
-
true
-
end
-
-
1
def ethical_approval_required?
-
2
(self.study_metadata.contains_human_dna == Study::YES &&
-
self.study_metadata.contaminated_human_dna == Study::NO &&
-
self.study_metadata.commercially_available == Study::NO)
-
end
-
-
1
def accession_service
-
if data_release_strategy == "open"
-
EraAccessionService.new
-
elsif data_release_strategy == "managed"
-
EgaAccessionService.new
-
else
-
NoAccessionService.new(self)
-
end
-
end
-
-
1
def validate_ena_required_fields!
-
self.validating_ena_required_fields = true
-
self.valid? or raise ActiveRecord::RecordInvalid, self
-
ensure
-
self.validating_ena_required_fields = false
-
end
-
-
1
def mailing_list_of_managers
-
receiver = self.managers.map(&:email).compact.uniq
-
receiver = User.all_administrators_emails if receiver.empty?
-
return receiver
-
end
-
-
# return true if yes, false if not , and nil if we don't know
-
1
def affiliated_with?(object)
-
case
-
when object.is_a?(Asset) && !object.is_a?(SampleTube)
-
#special case, no study assing we propagate to aliquot
-
nil
-
when object.respond_to?(:study_id)
-
self.id == object.study_id
-
when object.respond_to?(:studies)
-
object.studies.include?(self)
-
else
-
nil
-
end
-
end
-
-
#TODO rename, as it's not supposed to return a boolean but nil,[] or an object
-
1
def decide_if_object_should_be_taken(study_from, sample, object)
-
case study_from.affiliated_with?(object)
-
when true
-
case object
-
when Aliquot
-
object.sample == sample ? object : nil
-
else
-
object
-
end
-
when false then nil # we skip the object and its dependencies
-
else [] # don't return the object but check it's dependencies
-
end
-
end
-
1
private :decide_if_object_should_be_taken
-
-
# return the list of objects which haven't been successfully saved
-
# that's the caller responsibilitie to wrap the call in a transaction if needed
-
1
def take_sample(sample, study_from, user, asset_group)
-
errors = []
-
assets_to_move = sample.assets.select { |a| study_from.affiliated_with?(a) && a.is_a?(SampleTube) }
-
-
raise RuntimeError, "study_from not specified. Can't move a sample to a new study" unless study_from
-
helper = ->(object) { decide_if_object_should_be_taken(study_from, sample, object) }
-
objects_to_move =
-
sample.walk_objects(
-
:sample => [:receptacles, :study_samples],
-
:request => :item,
-
:"aliquot::receptacle" => :aliquots,
-
:asset => [ :requests, :parents, :children ],
-
:well => :plate,
-
:spikedbuffer => :skip_super,
-
&helper
-
)
-
-
Study.transaction do
-
#we duplicate each submission and reassign moved requests to it
-
objects_to_move.each do |object|
-
take_object(object, user, study_from)
-
begin
-
object.save!
-
rescue StandardError => ex
-
errors << ex.message
-
end
-
end
-
-
if asset_group
-
assets_to_move.each do |asset|
-
asset_groups = asset.asset_groups.reject { |ag| ag.study == study_from }
-
asset_groups << asset_group
-
asset.asset_groups = asset_groups
-
asset.save
-
end
-
end
-
end
-
if errors.present?
-
errors.each { |error | sample.errors.add("Move:", error) }
-
false
-
else
-
true
-
end
-
end
-
-
1
alias_attribute :friendly_name, :name
-
-
1
def subject_type
-
'study'
-
end
-
-
1
private
-
# beware , this method change the study of an object but doesn't look at some
-
# eventual dependencies.
-
1
def take_object(object, user, study_from)
-
# we don't check if the object is related to study from. because this can change
-
# if the object is related through and we have just changed the association
-
# (example asset and via Request)
-
case
-
when object.is_a?(Request)
-
# We shouldn't do study= because it's deprecated
-
# However we need to update the initial_study
-
# to do as if it was set this way initialy
-
object.initial_study = self
-
when object.respond_to?(:study=)
-
object.study = self
-
end
-
-
if object.respond_to?(:events)
-
object.events.create(
-
:message => (study_from ? "#{object.class.name} #{object.id} is moved from Study #{study_from.id} to Study #{self.id}" : "#{object.class.name} #{object.id} is moved to Study #{self.id}"),
-
:created_by => user.login,
-
:content => "#{object.class.name} moved by #{user.login}",
-
:of_interest_to => "administrators"
-
)
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
class StudyRelation < ActiveRecord::Base
-
belongs_to :study
-
belongs_to :related_study, :class_name => "Study"
-
belongs_to :study_relation_type
-
-
validates_presence_of :study
-
validates_presence_of :related_study
-
validates_presence_of :study_relation_type
-
-
validates_uniqueness_of :study_relation_type_id, :scope =>[:study_id, :related_study_id]
-
1
-
delegate :name, :reversed_name ,:to => :study_relation_type
-
-
-
1
module Associations
-
1
def self.included(base)
-
# Related studies
-
1
base.has_many :study_relations
-
1
base.has_many :related_studies, :through => :study_relations, :class_name => "Study"
-
#Inverse
-
1
base.has_many :reversed_study_relations, :class_name => "StudyRelation", :foreign_key => :related_study_id
-
1
base.has_many :reversed_related_studies, :through => :reversed_study_relations, :class_name => "Study", :source => :study
-
end
-
-
# related studies
-
1
def related_studies_for(relation_type)
-
r_id = relation_type.is_a?(StudyRelationType) ? relation_type.id : relation_type
-
study_relations.select { |r| r.study_relation_type_id == r_id }
-
end
-
-
1
def relations_for_study(study)
-
s_id = study.is_a?(Study) ? study.id : id
-
study_relations.select { |r| r.related_study_id == s_id }
-
end
-
-
1
def relation_types_for_study(study)
-
relations_for_study(study).map(&:study_relation_type)
-
end
-
-
# reverse related studies
-
1
def reversed_related_studies_for(relation_type)
-
r_id = relation_type.is_a?(StudyRelationType) ? relation_type.id : relation_type
-
reversed_study_relations.select { |r| r.study_relation_type_id == r_id }
-
end
-
-
1
def reversed_relations_for_study(study)
-
s_id = study.is_a?(Study) ? study.id : id
-
reversed_study_relations.select { |r| r.related_study_id == s_id }
-
end
-
-
1
def reversed_relation_types_for_study(study)
-
reversed_relations_for_study(study).map(&:study_relation_type)
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class StudyRelationType < ActiveRecord::Base
-
1
has_many :study_relations
-
1
validates_uniqueness_of :name
-
1
validates_uniqueness_of :reversed_name
-
-
1
def relate_studies!(study, related_study)
-
study.study_relations.create!(:related_study => related_study, :study_relation_type => self)
-
end
-
-
1
def self.names
-
all.map { |srt| [srt.name, srt.reversed_name] }.flatten
-
end
-
-
-
1
def self.relate_studies_by_name!(name, study, related_study)
-
relation_type = find_by_name(name)
-
return relation_type.relate_studies!(study, related_study) if relation_type
-
-
reversed = find_by_reversed_name(name)
-
return reversed.relate_studies!(related_study, study) if reversed
-
-
raise RuntimeError, "Can't find a study relation type with the name '#{name}'"
-
end
-
-
1
def self.unrelate_studies_by_name!(name, study, related_study)
-
relation_type = find_by_name(name)
-
relation = nil
-
if relation_type
-
relation = StudyRelation.find_by_study_relation_type_id_and_study_id_and_related_study_id(relation_type.id, study.id, related_study.id)
-
else # look for reverse one
-
relation_type = find_by_reversed_name(name)
-
relation = StudyRelation.find_by_study_relation_type_id_and_study_id_and_related_study_id(relation_type.id, related_study.id, study.id)
-
end
-
-
return relation.delete if relation
-
-
raise RuntimeError, "Can't find a study relation type with the name '#{name}'"
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class StudyReport < ActiveRecord::Base
-
1
extend DbFile::Uploader
-
-
1
class ProcessingError < Exception
-
end
-
-
1
self.per_page = 50
-
-
1
scope :for_study, ->(study) { { :conditions => { :study_id => study.id } } }
-
1
scope :for_user, ->(user) { { :conditions => { :user_id => user.id } } }
-
#named_scope :without_files, -> { select_without_file_columns_for(:report) }
-
-
1
has_uploaded :report, {:serialization_column => "report_filename"}
-
-
1
belongs_to :study
-
1
belongs_to :user
-
1
validates_presence_of :study
-
-
1
def headers
-
["Study","Sample Name","Plate","Supplier Volume","Supplier Concentration","Supplier Sample Name",
-
"Supplier Gender", "Concentration","Sequenome Count", "Sequenome Gender",
-
"Pico","Gel", "Qc Status", "Genotyping Status", "Genotyping Chip", "Is in Fluidigm"]
-
end
-
-
1
def perform
-
ActiveRecord::Base.transaction do
-
csv_options = {:row_sep => "\r\n", :force_quotes => true }
-
Tempfile.open("#{self.study.dehumanise_abbreviated_name}_progress_report.csv") do |tempfile|
-
Study.find(self.study_id).progress_report_on_all_assets do |fields|
-
tempfile.puts(CSV.generate_line(fields, csv_options))
-
end
-
tempfile.open # Reopen the temporary file
-
self.update_attributes!(:report => tempfile)
-
end
-
end
-
end
-
1
handle_asynchronously :perform, :priority => Proc.new {|i| i.priority }
-
-
1
def priority
-
configatron.delayed_job.fetch(:study_report_priority) || 100
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module StudyReport::AssetDetails
-
-
1
def qc_report
-
qc_data = {
-
:supplier_volume => 0
-
}
-
-
sample = primary_aliquot.try(:sample)
-
if sample.present?
-
if sample.empty_supplier_sample_name
-
supplier_sample_name = "Blank"
-
else
-
supplier_sample_name = sample.sample_metadata.supplier_name || sample.sanger_sample_id || sample.name
-
end
-
-
qc_data.merge!({
-
:supplier => sample.sample_manifest.try(:supplier).try(:name),
-
:sample_name => supplier_sample_name,
-
:sanger_sample_id => sample.sanger_sample_id,
-
:control => sample.control,
-
:status => (sample.updated_by_manifest ? 'Updated by manifest' : 'Awaiting manifest') ,
-
-
:supplier_gender => sample.sample_metadata.gender,
-
:cohort => sample.sample_metadata.cohort,
-
:country_of_origin => sample.sample_metadata.country_of_origin,
-
:geographical_region => sample.sample_metadata.geographical_region,
-
:ethnicity => sample.sample_metadata.ethnicity,
-
:dna_source => sample.sample_metadata.dna_source,
-
:is_resubmitted => sample.sample_metadata.is_resubmitted
-
})
-
end
-
-
qc_data
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
1
module StudyReport::StudyDetails
-
-
# This will pull out all well ids from stock plates in the study
-
1
def each_stock_well_id_in_study_in_batches(&block)
-
# Stock wells are determined by the requests leading from the stock plate
-
handle_wells(
-
"INNER JOIN requests ON requests.asset_id=assets.id",
-
"requests.initial_study_id",
-
PlatePurpose.find_all_by_name(['Stock Plate','Stock RNA Plate']).map(&:id),
-
&block
-
)
-
-
# Aliquot 1,2,3,4 & 5 plates are determined by the aliquots in their wells
-
handle_wells(
-
"INNER JOIN aliquots ON aliquots.receptacle_id=assets.id",
-
"aliquots.study_id",
-
PlatePurpose.find_all_by_name(['Aliquot 1','Aliquot 2','Aliquot 3','Aliquot 4','Aliquot 1']).map(&:id),
-
&block
-
)
-
end
-
-
1
def handle_wells(join, study_condition, plate_purpose_id, &block)
-
Asset.find_in_batches(
-
:select => 'DISTINCT assets.id',
-
:joins => [
-
"INNER JOIN container_associations ON assets.id=container_associations.content_id",
-
"INNER JOIN assets AS plates ON container_associations.container_id=plates.id AND plates.sti_type='Plate'",
-
join
-
],
-
:conditions => [
-
"plates.plate_purpose_id IN (?) AND #{study_condition}=?",
-
plate_purpose_id,
-
self.id
-
],
-
&block
-
)
-
end
-
1
private :handle_wells
-
-
1
def progress_report_header
-
[
-
"Status","Study","Supplier","Sanger Sample Name","Supplier Sample Name","Plate","Well","Supplier Volume",
-
"Supplier Gender", "Concentration","Initial Volume","Measured Volume","Total Micrograms","Sequenome Count", "Sequenome Gender",
-
"Pico","Gel", "Qc Status", "QC started date", "Pico date", "Gel QC date","Seq stamp date","Genotyping Status", "Genotyping Chip", "Genotyping Infinium Barcode", "Genotyping Barcode","Genotyping Well", "Cohort", "Country of Origin",
-
"Geographical Region","Ethnicity","DNA Source","Is Resubmitted","Control","Is in Fluidigm"
-
]
-
end
-
-
1
def progress_report_on_all_assets (&block)
-
block.call(progress_report_header)
-
each_stock_well_id_in_study_in_batches do |asset_ids|
-
-
# eager loading of well_attribute , can only be done on wells ...
-
Well.for_study_report.all(:conditions => {:id => asset_ids}).each do |asset|
-
asset_progress_data = asset.qc_report
-
next if asset_progress_data.nil?
-
-
block.call([
-
asset_progress_data[:status],
-
self.name,
-
asset_progress_data[:supplier],
-
asset_progress_data[:sanger_sample_id],
-
asset_progress_data[:sample_name],
-
asset_progress_data[:plate_barcode],
-
asset_progress_data[:well],
-
asset_progress_data[:supplier_volume],
-
asset_progress_data[:supplier_gender],
-
asset_progress_data[:concentration],
-
asset_progress_data[:initial_volume],
-
asset_progress_data[:measured_volume],
-
asset_progress_data[:quantity],
-
asset_progress_data[:sequenom_count],
-
(asset_progress_data[:sequenom_gender]||[]).join(''),
-
asset_progress_data[:pico],
-
asset_progress_data[:gel],
-
asset_progress_data[:qc_status],
-
asset_progress_data[:qc_started_date],
-
asset_progress_data[:pico_date],
-
asset_progress_data[:gel_qc_date],
-
asset_progress_data[:sequenom_stamp_date],
-
asset_progress_data[:genotyping_status],
-
asset_progress_data[:genotyping_plate_purpose],
-
asset_progress_data[:genotyping_infinium_barcode],
-
asset_progress_data[:genotyping_barcode],
-
asset_progress_data[:genotyping_well],
-
asset_progress_data[:cohort],
-
asset_progress_data[:country_of_origin],
-
asset_progress_data[:geographical_region],
-
asset_progress_data[:ethnicity],
-
asset_progress_data[:dna_source],
-
asset_progress_data[:is_resubmitted],
-
asset_progress_data[:control],
-
asset_progress_data[:is_in_fluidigm]
-
])
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
module StudyReport::WellDetails
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :for_study_report, -> { includes([
-
:map, :well_attribute, :events, :target_wells, { :plate => :plate_purpose, :primary_aliquot => { :sample => :sample_metadata } }
-
])}
-
end
-
end
-
-
1
def dna_qc_request_status
-
requests_status = dna_qc_requests_status
-
return nil if requests_status.blank?
-
most_recent_status = requests_status.last
-
return "failed" if most_recent_status == "blocked"
-
-
most_recent_status
-
end
-
-
1
def dna_qc_requests_status
-
requests_status(RequestType.dna_qc)
-
end
-
-
1
def genotyping_requests_status
-
requests_status(RequestType.genotyping)
-
end
-
-
1
def genotyping_status
-
primary_aliquot.present? ? primary_aliquot.sample.genotyping_done : ''
-
end
-
-
1
def qc_report
-
# well must be from a stock plate
-
return nil if !(self.plate && self.plate.stock_plate?)
-
qc_data = super
-
-
qc_data.merge!({
-
:well => self.map.description,
-
:concentration => self.well_attribute.concentration,
-
:sequenom_count => "#{self.get_sequenom_count.to_i}/30",
-
:sequenom_gender => self.get_gender_markers,
-
:pico => self.well_attribute.pico_pass,
-
:is_in_fluidigm => self.is_in_fluidigm?,
-
:gel => self.well_attribute.gel_pass,
-
:plate_barcode => self.plate.barcode,
-
:measured_volume => self.well_attribute.measured_volume,
-
:gel_qc_date => self.gel_qc_date,
-
:pico_date => self.pico_date,
-
:qc_started_date => self.plate.qc_started_date,
-
:sequenom_stamp_date => self.plate.sequenom_stamp_date,
-
:quantity => self.well_attribute.quantity_in_micro_grams.try(:round,3),
-
:initial_volume => self.well_attribute.initial_volume
-
})
-
qc_data[:genotyping_status] = self.genotyping_status
-
qc_data[:genotyping_barcode] = self.primary_aliquot.sample.genotyping_snp_plate_id if primary_aliquot.present?
-
-
child_plate = self.find_child_plate
-
if child_plate && child_plate.respond_to?(:plate)
-
if child_plate.plate && child_plate.plate.plate_purpose
-
qc_data[:genotyping_plate_purpose] = child_plate.plate.plate_purpose.name
-
qc_data[:genotyping_infinium_barcode] = child_plate.plate.plate_metadata.infinium_barcode
-
qc_data[:genotyping_barcode] = child_plate.plate.barcode if child_plate.plate.barcode
-
qc_data[:genotyping_well] = child_plate.try(:map).try(:description) if child_plate.plate.barcode
-
end
-
end
-
qc_data[:qc_status] = dna_qc_request_status
-
-
qc_data
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class StudySample < ActiveRecord::Base
-
1
include Api::StudySampleIO::Extensions
-
-
1
self.per_page = 500
-
1
include Uuid::Uuidable
-
-
1
belongs_to :study
-
1
belongs_to :sample
-
-
-
1
validates_uniqueness_of :sample_id, :scope => [:study_id], :message => "cannot be added to the same study more than once"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class StudyType < ActiveRecord::Base
-
1
extend Attributable::Association::Target
-
-
1
has_many :study
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name, :message => "of study type already present in database"
-
-
-
1
def for_select_dropdown
-
valid_for_creation? ? [self.name, self.id] : nil
-
end
-
-
1
def self.include?(studytype_name)
-
study_type = StudyType.find_by_name(studytype_name)
-
unless study_type.nil?
-
return study_type.valid_type
-
end
-
return false
-
end
-
-
1
module Associations
-
1
def self.included(base)
-
1
base.validates_presence_of :study_type_id
-
1
base.belongs_to :study_type
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class SubclassAttribute < ActiveRecord::Base
-
1
belongs_to :attributable, :polymorphic => true
-
-
1
validates_uniqueness_of :name, :scope => :attributable_id
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
class Submission < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
extend Submission::StateMachine
-
1
include Submission::DelayedJobBehaviour
-
1
include ModelExtensions::Submission
-
#TODO[mb14] check if really needed. We use them in project_test
-
1
include Request::Statistics::DeprecatedMethods
-
1
include Submission::Priorities
-
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
# Created during the lifetime ...
-
1
has_many :requests, :inverse_of => :submission
-
1
has_many :items, :through => :requests
-
-
1
has_many :orders, :inverse_of => :submission
-
1
has_many :studies, :through => :orders
-
1
accepts_nested_attributes_for :orders, :update_only => true
-
-
1
has_many :comments_from_requests, :through => :requests, :source => :comments
-
-
1
def comments
-
# has_many throug doesn't work. Comments is a column (string) of order
-
# not an ActiveRecord
-
orders.map(&:comments).flatten(1).compact
-
end
-
-
1
def add_comment(description,user)
-
requests.where_is_not_a?(TransferRequest).map do |request|
-
request.add_comment(description,user)
-
end
-
end
-
-
-
1
self.per_page = 500
-
1
scope :including_associations_for_json, -> { includes([
-
:uuid_object,
-
{:orders => [
-
{:project => :uuid_object},
-
{:assets => :uuid_object },
-
{:study => :uuid_object },
-
:user]}
-
])}
-
-
1
scope :building, -> { where( :state => "building" ) }
-
1
scope :pending, -> { where( :state => "pending" ) }
-
1
scope :ready, -> { where( :state => "ready" ) }
-
-
1
scope :latest_first, -> { order('id DESC') }
-
-
1
before_destroy :building?, :empty_of_orders?
-
-
1
def empty_of_orders?
-
self.orders.empty?
-
end
-
-
# Before destroying this instance we should cancel all of the requests it has made
-
1
before_destroy :cancel_all_requests_on_destruction
-
-
1
PER_ORDER_REQUEST_OPTIONS = ['pre_capture_plex_level','gigabases_expected']
-
-
1
def cancel_all_requests_on_destruction
-
ActiveRecord::Base.transaction do
-
requests.all.each do |request|
-
request.submission_cancelled! # Cancel first to prevent event doing something stupid
-
request.events.create!(:message => "Submission #{self.id} as destroyed")
-
end
-
end
-
end
-
1
private :cancel_all_requests_on_destruction
-
-
1
def cancel_all_requests
-
ActiveRecord::Base.transaction do
-
requests.each(&:submission_cancelled!)
-
end
-
end
-
1
private :cancel_all_requests
-
-
1
def requests_cancellable?
-
requests.all?(&:cancellable?)
-
end
-
-
1
def self.render_class
-
Api::SubmissionIO
-
end
-
-
1
def url_name
-
"submission"
-
end
-
1
alias_method(:json_root, :url_name)
-
-
1
def subject_type
-
'submission'
-
end
-
1
alias_attribute :friendly_name, :name
-
-
1
def self.build!(options)
-
submission_options = {}
-
[:message, :priority].each do |option|
-
value = options.delete(option)
-
submission_options[option] = value if value
-
end
-
ActiveRecord::Base.transaction do
-
order = Order.prepare!(options)
-
order.create_submission({:user_id => order.user_id}.merge(submission_options))
-
order.save! #doesn't save submission id otherwise
-
study_name = order.study.try(:name)
-
order.submission.update_attributes!(:name=>study_name) if study_name
-
order.submission.reload
-
order.submission.built!
-
order.submission
-
end
-
end
-
# TODO[xxx]: ... to here really!
-
-
1
def safe_to_delete?
-
ActiveSupport::Deprecation.warn "Submission#safe_to_delete? may not recognise all states"
-
unless self.ready?
-
requests_in_progress = self.requests.select{|r| r.state != 'pending' || r.state != 'waiting'}
-
requests_in_progress.empty? ? true : false
-
else
-
return true
-
end
-
end
-
-
1
def process_submission!
-
# for now, we just delegate the requests creation to orders
-
ActiveRecord::Base.transaction do
-
multiplexing_assets = nil
-
orders.each do |order|
-
order.build_request_graph!(multiplexing_assets) { |a| multiplexing_assets ||= a }
-
end
-
-
PreCapturePool::Builder.new(self).build!
-
-
errors.add(:requests, "No requests have been created for this submission") if requests.empty?
-
raise ActiveRecord::RecordInvalid, self if errors.present?
-
end
-
-
end
-
1
alias_method(:create_requests, :process_submission!)
-
-
1
def multiplexed?
-
orders.any? { |o| RequestType.find(o.request_types).any?(&:for_multiplexing?) }
-
end
-
-
-
1
def multiplex_started_passed
-
multiplex_started_passed_result = false
-
if self.multiplexed?
-
requests = Request.find_all_by_submission_id(self.id)
-
states = requests.map(&:state).uniq
-
if ( states.include?("started") || states.include?("passed") )
-
multiplex_started_passed_result = true
-
end
-
end
-
return multiplex_started_passed_result
-
end
-
-
1
def duplicate(&block)
-
raise "Not implemented yet"
-
-
create_parameters = template_parameters
-
new_submission = Submission.create(create_parameters.merge( :study => self.study,:workflow => self.workflow,
-
:user => self.user, :assets => self.assets, :state => self.state,
-
:request_types => self.request_types,
-
:request_options => self.request_options,
-
:comments => self.comments,
-
:project_id => self.project_id), &block)
-
new_submission.save
-
return new_submission
-
end
-
-
#Required at initial construction time ...
-
1
validate :validate_orders_are_compatible
-
-
#Order needs to have the 'structure'
-
1
def validate_orders_are_compatible()
-
return true if orders.size < 2
-
# check every order agains the first one
-
first_order = orders.first
-
orders[1..-1].each { |o| check_orders_compatible?(o,first_order) }
-
return false if errors.count > 0
-
end
-
1
private :validate_orders_are_compatible
-
-
# this method is part of the submission
-
# not order, because it is submission
-
# which decide if orders are compatible or not
-
1
def check_orders_compatible?(a,b)
-
errors.add(:request_types, "are incompatible") if a.request_types != b.request_types
-
errors.add(:request_options, "are incompatible") if !request_options_compatible?(a,b)
-
errors.add(:item_options, "are incompatible") if a.item_options != b.item_options
-
check_studies_compatible?(a.study, b.study)
-
end
-
-
1
def request_options_compatible?(a,b)
-
a.request_options.reject {|k,_| PER_ORDER_REQUEST_OPTIONS.include?(k) } == b.request_options.reject {|k,_| PER_ORDER_REQUEST_OPTIONS.include?(k) }
-
end
-
-
1
def check_studies_compatible?(a,b)
-
errors.add(:study, "Can't mix contaminated and non contaminated human DNA") unless a.study_metadata.contaminated_human_dna == b.study_metadata.contaminated_human_dna
-
errors.add(:study, "Can't mix X and autosome removal with non-removal") unless a.study_metadata.remove_x_and_autosomes == b.study_metadata.remove_x_and_autosomes
-
end
-
-
#for the moment we consider that request types should be the same for all order
-
#so we can take the first one
-
1
def request_type_ids
-
return [] unless orders.size >= 1
-
orders.first.request_types.map(&:to_i)
-
end
-
-
-
1
def next_request_type_id(request_type_id)
-
request_type_ids[request_type_ids.index(request_type_id)+1] if request_type_ids.present?
-
end
-
-
1
def previous_request_type_id(request_type_id)
-
request_type_ids[request_type_ids.index(request_type_id)-1] if request_type_ids.present?
-
end
-
-
1
def obtain_next_requests_to_connect(request, next_request_type_id=nil)
-
if next_request_type_id.nil?
-
next_request_type_id = self.next_request_type_id(request.request_type_id) or return []
-
end
-
all_requests = requests.with_request_type_id([ request.request_type_id, next_request_type_id ]).all(:order => 'id ASC')
-
sibling_requests, next_possible_requests = all_requests.partition { |r| r.request_type_id == request.request_type_id }
-
-
if request.request_type.for_multiplexing?
-
# If we have no pooling behaviour specified, then we're pooling by submission.
-
# We keep to the existing behaviour, to isolate risk
-
return next_possible_requests if request.request_type.pooling_method.nil?
-
# If we get here we've got custom pooling behaviour defined.
-
index = request.request_type.pool_index_for_request(request)
-
number_to_return = next_possible_requests.count / request.request_type.pool_count
-
return next_possible_requests.slice(index*number_to_return,number_to_return)
-
-
else
-
# If requests aren't multiplexed, then they may be batched separately, and we'll have issues
-
# if downstream changes affect the ratio. We can use the multiplier on order however, as we
-
# don't need to worry about divergence ratios f < 1
-
# Determine the number of requests that should come next from the multipliers in the orders.
-
# NOTE: This will only work whilst you order the same number of requests.
-
multipliers = orders.map { |o| (o.request_options[:multiplier].try(:[], next_request_type_id.to_s) || 1).to_i }.compact.uniq
-
raise RuntimeError, "Mismatched multiplier information for submission #{id}" if multipliers.size != 1
-
# Now we can take the group of requests from next_possible_requests that tie up.
-
divergence_ratio = multipliers.first
-
index = sibling_requests.index(request)
-
next_possible_requests[index*divergence_ratio,[ 1, divergence_ratio ].max]
-
end
-
end
-
-
1
def next_requests(request)
-
# We should never be receiving requests that are not part of our request graph.
-
raise RuntimeError, "Request #{request.id} is not part of submission #{id}" unless request.submission_id == self.id
-
-
# Pick out the siblings of the request, so we can work out where it is in the list, and all of
-
# the requests in the subsequent request type, so that we can tie them up. We order by ID
-
# here so that the earliest requests, those created by the submission build, are always first;
-
# any additional requests will have come from a sequencing batch being reset.
-
next_request_type_id = self.next_request_type_id(request.request_type_id) or return []
-
return request.target_asset.requests.find(:all,:conditions=>{:submission_id=>id,:request_type_id=>next_request_type_id}) if request.target_asset.present?
-
obtain_next_requests_to_connect(request, next_request_type_id)
-
end
-
-
1
def name
-
name = attributes['name'] || study_names
-
name.present? ? name : "##{id}"
-
end
-
-
1
def study_names
-
# TODO: Should probably be re-factored, although we'll only fall back to the intensive code in the case of cross study re-requests
-
orders.map {|o| o.study.try(:name)||o.assets.map{|a| a.aliquots.map {|al| al.study.try(:name) }} }.flatten.compact.sort.uniq.join("|")
-
end
-
-
1
def cross_project?
-
multiplexed? && orders.map(&:project_id).uniq.size > 1
-
end
-
-
1
def cross_study?
-
multiplexed? && orders.map(&:study_id).uniq.size > 1
-
end
-
-
end
-
-
1
class Array
-
1
def intersperse(separator)
-
(inject([]) { |a,v| a+[v,separator] })[0...-1]
-
end
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014 Genome Research Ltd.
-
1
module Submission::AccessionBehaviour
-
1
def self.included(base)
-
1
base.class_eval do
-
1
validate :check_data_release_and_accession_for_submission, :if => :can_check_data_release_and_accession?
-
end
-
end
-
-
1
def can_check_data_release_and_accession?
-
self.study.present? && self.request_types_require_accessioning?
-
end
-
-
1
def request_types_require_accessioning?
-
RequestType.find(self.request_types).detect(&:accessioning_required?)
-
end
-
-
1
def check_data_release_and_accession_for_submission
-
return if configatron.disable_accession_check == true
-
-
if not study.valid_data_release_properties?
-
errors.add(:study,"#{study.name}: Please fill in the study data release information")
-
elsif not study.ena_accession_required?
-
# Nothing to do here because the study does not require ENA accessioning
-
elsif not study.accession_number?
-
errors.add(:study,"#{study.name} and all samples must have accession numbers")
-
elsif not all_samples_have_accession_numbers?
-
errors.add(:base,"Samples #{unaccessioned_samples} are missing accession numbers")
-
end
-
end
-
-
1
private
-
-
1
def test_asset_group
-
AssetGroup.new(:assets => self.assets)
-
end
-
-
1
def unaccessioned_samples
-
test_asset_group.unaccessioned_samples.map(&:name).to_sentence
-
end
-
-
1
def all_samples_have_accession_numbers?
-
test_asset_group.all_samples_have_accession_numbers?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module Submission::AssetGroupBehaviour
-
1
def self.included(base)
-
1
base.class_eval do
-
1
belongs_to :asset_group
-
1
before_create :find_asset_group, :unless => :asset_group?
-
1
before_create :pull_assets_from_asset_group, :if => :asset_group?
-
-
# Required once out of the building state ...
-
1
validates_presence_of :assets, :if => :assets_need_validating?
-
-
end
-
end
-
-
# Assets need validating if we are putting this order into a submission and the asset group has not been
-
# specified in some form.
-
1
def assets_need_validating?
-
not building? and not (asset_group? or not asset_group_name.blank?)
-
end
-
1
private :assets_need_validating?
-
-
1
def complete_building
-
create_our_asset_group unless asset_group? or self.assets.blank?
-
super
-
end
-
-
1
def asset_group?
-
self.asset_group_id.present? or self.asset_group.present?
-
end
-
1
private :asset_group?
-
-
1
def pull_assets_from_asset_group
-
self.assets = self.asset_group.assets unless self.asset_group.assets.empty?
-
true
-
end
-
1
private :pull_assets_from_asset_group
-
-
# NOTE: We cannot name this method 'create_asset_group' because that's provided by 'has_one :asset_group'!
-
1
def create_our_asset_group
-
return nil if self.study.nil? && cross_study_allowed
-
group_name = self.asset_group_name
-
group_name = self.uuid if asset_group_name.blank?
-
-
asset_group = self.study.asset_groups.create!(
-
:name => group_name,
-
:user => self.user,
-
:assets => self.assets
-
)
-
self.update_attributes!(:asset_group_id => asset_group.id)
-
end
-
1
private :create_our_asset_group
-
-
1
def find_asset_group
-
self.asset_group = self.study.asset_groups.first(:conditions => { :name => asset_group_name }) unless asset_group_name.blank?
-
true
-
end
-
1
private :find_asset_group
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
1
module Submission::Crossable
-
1
def cross_study_allowed
-
assets.any? {|a| a.studies.uniq.count > 1 }
-
end
-
-
1
def cross_project_allowed
-
assets.any? {|a| a.projects.uniq.count > 1 }
-
end
-
-
1
def cross_compatible?
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014 Genome Research Ltd.
-
1
module Submission::DelayedJobBehaviour
-
1
def self.included(base)
-
1
base.class_eval do
-
1
conf_priority = configatron.delayed_job.fetch(:submission_process_priority)
-
1
priority = conf_priority.present? ? conf_priority : 0
-
1
handle_asynchronously :build_batch, :priority => priority
-
end
-
end
-
-
1
def complete_building
-
super
-
build_batch
-
end
-
-
1
def build_batch
-
ActiveRecord::Base.transaction do
-
finalize_build!
-
end
-
rescue Submission::ProjectValidation::Error => project_exception
-
fail_set_message_and_save(project_exception.message)
-
rescue ActiveRecord::StatementInvalid => sql_exception
-
# If an SQL problems occurs, it's more likely that's it's
-
# a one shot one, e.g. timeout , deadlock etc ...
-
# So we don't want the submission to fail but the delayed job to
-
# retry later. Therefore the DelayedJob should fail
-
raise sql_exception
-
rescue ActiveRecord::RecordInvalid => exception
-
fail_set_message_and_save(exception.message)
-
rescue => exception
-
fail_set_message_and_save("#{exception.message}\n#{exception.backtrace.join("\n")}")
-
end
-
-
1
def finalize_build!
-
self.process!
-
self.ready!
-
end
-
-
1
def fail_set_message_and_save(message)
-
self.fail!
-
self.message = message[0..254]
-
self.save(:validate => false) # Just in case the cause is it being invalid!
-
end
-
1
private :fail_set_message_and_save
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module Submission::FlexibleRequestGraph
-
-
# A doublet couples a source asset to a particular qc metric.
-
# This allows us to pass the qc_metric downstream, without relying
-
# on maintaining assets at each step. This is important as not only
-
# to some requests generate their assets on the fly, but we'd need
-
# to make sure that we had all appropriate well links and asset links
-
# in place.
-
Doublet = Struct.new(:asset,:qc_metric)
-
-
class RequestChainError < RuntimeError; end
-
-
class RequestChain
-
-
attr_reader :order, :source_assets_qc_metrics, :preplexed, :built, :multiplexed
-
alias_method :built?, :built
-
alias_method :multiplexed?, :multiplexed
-
alias_method :preplexed?, :preplexed
-
1
-
delegate :product, :to => :order
-
-
1
def initialize(order,source_assets, multiplexing_assets)
-
@order = order
-
@source_assets_qc_metrics = source_assets.map {|asset| Doublet.new(asset,[asset.latest_stock_metric(product)]) }
-
@multiplexing_assets = multiplexing_assets
-
@preplexed = multiplexing_assets.present?
-
@built = false
-
@multiplexed = false
-
end
-
-
1
def build!
-
raise RequestChainError, "Request chains can only be built once" if built?
-
raise StandardError, 'No request types specified!' if request_types.empty?
-
request_types.inject(source_assets_qc_metrics) do |source_assets_qc_metrics_memo,request_type|
-
link = ChainLink.build!(request_type,multiplier_for(request_type),source_assets_qc_metrics_memo,self)
-
break if preplexed && link.multiplexed?
-
link.target_assets_qc_metrics
-
end
-
@built = true
-
end
-
-
##
-
# multiplexing_assets returns @multiplexing_assets if present
-
# otherwise it yields to any presented block and assumes it returns
-
# the multiplexing_assets
-
1
def multiplexing_assets
-
@multiplexed = true
-
-
@multiplexing_assets ||= yield if block_given?
-
@multiplexing_assets
-
end
-
-
1
private
-
-
1
def request_types
-
order.request_types.map {|request_type_id| RequestType.find(request_type_id) }
-
end
-
-
1
def multiplier_for(request_type)
-
multipliers[request_type.id.to_s]
-
end
-
-
1
def multipliers
-
@multipliers ||= Hash.new { |h,k| h[k] = 1 }.tap do |multipliers|
-
requested_multipliers = order.request_options.try(:[], :multiplier) || {}
-
requested_multipliers.each { |k,v| multipliers[k.to_s] = v.to_i }
-
end
-
end
-
-
end
-
-
##
-
# Builds all requests of a given request type and any target_assets
-
# The build! method automatically creates a link of the appropriate class
-
1
module ChainLink
-
-
1
def self.included(base)
-
2
base.class_eval do
-
2
attr_reader :request_type, :multiplier, :source_assets_qc_metrics, :target_assets_qc_metrics, :chain
-
end
-
end
-
-
1
def self.build!(request_type,multiplier,source_assets_qc_metrics,chain)
-
link_class = request_type.for_multiplexing? ? MultiplexedLink : UnplexedLink
-
link_class.new(request_type,multiplier,source_assets_qc_metrics,chain).tap do |link|
-
link.build!
-
end
-
end
-
-
1
def initialize(request_type,multiplier,source_assets_qc_metrics,chain)
-
@request_type = request_type
-
@multiplier = multiplier
-
@source_assets_qc_metrics = source_assets_qc_metrics
-
@chain = chain
-
end
-
-
1
def multiplexed?; false; end
-
-
1
def build!
-
multiplier.times do |_|
-
# Now we can iterate over the source assets and target assets building the requests between them.
-
# Ensure that the request has the correct comments on it, and that the aliquots of the source asset
-
# are transferred into the destination if the request does not do this in some manner itself.
-
source_asset_metrics_target_assets do |source_asset, qc_metrics, target_asset|
-
-
chain.order.create_request_of_type!(
-
request_type,
-
:asset => source_asset, :target_asset => target_asset
-
).tap do |request|
-
-
AssetLink.create_edge!(source_asset, target_asset) if source_asset.present? and target_asset.present?
-
-
request.qc_metrics = qc_metrics.compact.uniq
-
request.update_responsibilities!
-
-
comments.each do |comment|
-
request.comments.create!(:user => user, :description => comment)
-
end if comments.present?
-
end
-
end
-
end
-
associate_built_requests!
-
end
-
-
1
def target_assets
-
target_assets_qc_metrics.map(&:asset).flatten.uniq
-
end
-
-
1
private
-
-
1
def comments
-
chain.order.comments
-
end
-
-
1
def user
-
chain.order.user
-
end
-
-
1
def source_asset_metrics_target_assets
-
new_target_assets = generate_target_assets
-
source_assets_doublet_with_index do |doublet,index|
-
yield(doublet.asset,doublet.qc_metric,new_target_assets[index].asset)
-
end
-
end
-
-
1
def associate_built_requests!
-
#Do Nothing
-
end
-
-
1
def create_target_asset(source_asset = nil)
-
request_type.create_target_asset! do |asset|
-
asset.generate_barcode
-
asset.generate_name(source_asset.try(:name) || asset.barcode.to_s)
-
end
-
end
-
end
-
-
1
class MultiplexedLink
-
1
include ChainLink
-
-
1
def initialize(request_type,multiplier,assets,chain)
-
raise RequestChainError unless request_type.for_multiplexing?
-
raise RequestChainError, 'Cannot multiply multiplexed requests' if multiplier > 1
-
super
-
end
-
-
1
def multiplexed?; true; end
-
-
1
private
-
-
1
def source_assets_doublet_with_index
-
source_assets_qc_metrics.each do |doublet|
-
yield(doublet,request_type.pool_index_for_asset(doublet.asset))
-
end
-
end
-
-
# We can only do this once for multiplexed request types
-
1
def generate_target_assets
-
@target_assets_qc_metrics ||= chain.multiplexing_assets do
-
# We yield only if we don't have any multiplexing assets
-
all_qc_metrics = source_assets_qc_metrics.map {|doublet| doublet.qc_metric }.flatten.uniq
-
request_type.pool_count.times.map { Doublet.new(create_target_asset,all_qc_metrics) }
-
end
-
end
-
-
1
def associate_built_requests!
-
downstream_requests.each do |request|
-
request.update_attributes!(:initial_study => nil) if request.initial_study != study
-
request.update_attributes!(:initial_project => nil) if request.initial_project != project
-
comments.each do |comment|
-
request.comments.create!(:user => user, :description => comment)
-
end if comments.present?
-
end
-
end
-
-
1
def downstream_requests
-
target_assets.uniq.compact.map(&:requests).flatten
-
end
-
-
end
-
-
1
class UnplexedLink
-
1
include ChainLink
-
-
1
def initialize(request_type,multiplier,assets,chain)
-
raise RequestChainError if request_type.for_multiplexing?
-
super
-
end
-
-
1
def generate_target_assets
-
source_assets_qc_metrics.map do |doublet|
-
Doublet.new(create_target_asset(doublet.asset),doublet.qc_metric)
-
end.tap do |new_target_assets|
-
@target_assets_qc_metrics ||= []
-
@target_assets_qc_metrics.concat(new_target_assets)
-
end
-
end
-
-
1
def source_assets_doublet_with_index(&block)
-
source_assets_qc_metrics.each_with_index do |doublet,index|
-
yield(doublet, index)
-
end
-
end
-
end
-
-
1
module OrderMethods
-
-
1
def build_request_graph!(multiplexing_assets = nil, &block)
-
ActiveRecord::Base.transaction do
-
RequestChain.new(self,assets, multiplexing_assets).tap do |chain|
-
chain.build!
-
yield chain.multiplexing_assets if chain.multiplexed?
-
end
-
end
-
end
-
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
# This module can be included where the submission has a linear behaviour, with no branching.
-
1
module Submission::LinearRequestGraph
-
# TODO: When Item dies this code will not need to hand it around so much!
-
-
# Builds the entire request graph for this submission. If you want to reuse the multiplexing assets then
-
# pass them in as the 'multiplexing_assets' parameter; specify a block if you want to know when they have
-
# been used.
-
1
def build_request_graph!(multiplexing_assets = nil, &block)
-
ActiveRecord::Base.transaction do
-
create_request_chain!(
-
build_request_type_multiplier_pairs,
-
assets.map { |asset| [ asset, [asset.latest_stock_metric(product)], create_item_for!(asset) ] },
-
multiplexing_assets,
-
&block
-
)
-
end
-
end
-
-
# Generates a list of RequestType and multiplier pairs for the instance.
-
1
def build_request_type_multiplier_pairs
-
# Ensure that the keys of the multipliers hash are strings, otherwise we get weirdness!
-
multipliers = Hash.new { |h,k| h[k] = 1 }.tap do |multipliers|
-
requested_multipliers = request_options.try(:[], :multiplier) || {}
-
requested_multipliers.each { |k,v| multipliers[k.to_s] = v.to_i }
-
end
-
-
request_types.dup.map do |request_type_id|
-
[ RequestType.find(request_type_id), multipliers[request_type_id.to_s] ]
-
end
-
end
-
1
private :build_request_type_multiplier_pairs
-
-
1
def create_target_asset_for!(request_type, source_asset = nil)
-
request_type.create_target_asset! do |asset|
-
asset.generate_barcode
-
asset.generate_name(source_asset.try(:name) || asset.barcode.to_s)
-
end
-
end
-
1
private :create_target_asset_for!
-
-
1
class MockedArray
-
1
def initialize(contents)
-
@contents = contents
-
end
-
1
def [](_)
-
@contents
-
end
-
1
def uniq
-
[@contents]
-
end
-
end
-
-
# Creates the next step in the request graph, taking the first request type specified and building
-
# enough requests for the source requests. It will recursively call itself if there are more requests
-
# that need creating.
-
1
def create_request_chain!(request_type_and_multiplier_pairs, source_asset_qc_metric_and_item, multiplexing_assets, &block)
-
raise StandardError, 'No request types specified!' if request_type_and_multiplier_pairs.empty?
-
request_type, multiplier = request_type_and_multiplier_pairs.shift
-
-
multiplier.times do |_|
-
# If the request type is for multiplexing it means that all of the assets end up in one target asset.
-
# Otherwise there are the same number of target assets as source.
-
target_assets =
-
if request_type.for_multiplexing?
-
multiplexing_assets || MockedArray.new(create_target_asset_for!(request_type))
-
else
-
source_asset_qc_metric_and_item.map { |source_asset, _| create_target_asset_for!(request_type, source_asset) }
-
end
-
yield(target_assets) if block_given? and request_type.for_multiplexing?
-
-
# Now we can iterate over the source assets and target assets building the requests between them.
-
# Ensure that the request has the correct comments on it, and that the aliquots of the source asset
-
# are transferred into the destination if the request does not do this in some manner itself.
-
source_asset_qc_metric_and_item.each_with_index do |(source_asset, qc_metrics, item), index|
-
target_asset = target_assets[index]
-
-
create_request_of_type!(
-
request_type,
-
:asset => source_asset, :target_asset => target_asset, :item => item
-
).tap do |request|
-
# TODO: AssetLink is supposed to disappear at some point in the future because it makes no real sense
-
# given that the request graph describes this relationship.
-
AssetLink.create_edge!(source_asset, target_asset) if source_asset.present? and target_asset.present?
-
-
request.qc_metrics = qc_metrics.compact.uniq
-
request.update_responsibilities!
-
-
comments.split("\n").each do |comment|
-
request.comments.create!(:user => user, :description => comment)
-
end if comments.present?
-
end
-
end
-
-
# Now we can continue to the next request type in the chain, using the target assets we've created.
-
# We need to de-duplicate the multiplexed assets. Note that we duplicate the pairs here so that
-
# they don't get disrupted by the shift operation at the start of this method.
-
next if request_type_and_multiplier_pairs.empty?
-
-
target_assets_items = if request_type.for_multiplexing? # May have many nil assets for non-multiplexing
-
if multiplexing_assets.nil?
-
criteria = source_asset_qc_metric_and_item.map {|sci| sci[1] }.flatten.uniq
-
target_assets.uniq.map { |asset| [ asset, criteria, nil ] }
-
else
-
associate_built_requests(target_assets.uniq.compact); []
-
end
-
else
-
target_assets.each_with_index.map do |asset,index|
-
source_asset = request_type.no_target_asset? ? source_asset_qc_metric_and_item[index].first : asset
-
[ source_asset, source_asset_qc_metric_and_item[index][1], source_asset_qc_metric_and_item[index].last ]
-
end
-
end
-
-
create_request_chain!(request_type_and_multiplier_pairs.dup, target_assets_items, multiplexing_assets, &block)
-
end
-
end
-
1
private :create_request_chain!
-
-
1
def associate_built_requests(assets)
-
assets.map(&:requests).flatten.each do |request|
-
request.update_attributes!(:initial_study => nil) if request.initial_study != study
-
request.update_attributes!(:initial_project => nil) if request.initial_project != project
-
comments.split("\n").each do |comment|
-
request.comments.create!(:user => user, :description => comment)
-
end if comments.present?
-
end
-
end
-
1
private :associate_built_requests
-
-
# TODO: Remove this it's not supposed to be being used!
-
1
def create_item_for!(asset)
-
item = nil
-
item = asset.requests.first.item unless asset.requests.empty?
-
return item if item.present?
-
-
Item.create!(:workflow => workflow, :name => "#{asset.display_name} #{id.to_s}", :submission => self.submission)
-
end
-
1
private :create_item_for!
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2014 Genome Research Ltd.
-
1
class OrderPresenter
-
1
ATTRIBUTES = [
-
:study_id,
-
:project_name,
-
:plate_purpose_id,
-
:sample_names_text,
-
:lanes_of_sequencing_required,
-
:comments,
-
]
-
-
1
attr_accessor *ATTRIBUTES
-
-
1
def initialize(order)
-
@target_order = order
-
end
-
-
# id needs to be defined to stop Object#id being called on the OrderPresenter
-
# instance.
-
1
def id
-
@target_order.id
-
end
-
-
1
def method_missing(method, *args, &block)
-
@target_order.send(method, *args, &block)
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
1
class Submission::PresenterSkeleton
-
1
class_attribute :attributes, :instance_writer => false
-
1
self.attributes = Array.new
-
-
1
def initialize(user, submission_attributes = {})
-
submission_attributes = {} if submission_attributes.blank?
-
-
@user = user
-
-
attributes.each do |attribute|
-
send("#{attribute}=", submission_attributes[attribute])
-
end
-
-
end
-
-
# id accessors need to be explicitly defined...
-
1
def id
-
@id
-
end
-
-
1
def id=(submission_id)
-
@id = submission_id
-
end
-
-
1
def lanes_of_sequencing
-
return lanes_from_request_options if %{building pending}.include?(submission.state)
-
lanes_from_request_counting
-
end
-
-
1
def cross_compatible?
-
end
-
-
1
def order_studies
-
if order.study
-
yield(order.study.name, order.study)
-
else # Cross study
-
Study.in_assets(order.all_assets).each do |study|
-
yield(study.name,study)
-
end
-
end
-
end
-
-
1
def order_projects
-
if order.project
-
yield(order.project.name, order.project)
-
else # Cross Project
-
Project.in_assets(order.all_assets).each do |project|
-
yield(project.name,project)
-
end
-
end
-
end
-
-
1
def lanes_from_request_options
-
return order.request_options.fetch(:multiplier, {}).values.last||1 if order.request_types[-2].nil?
-
-
sequencing_request = RequestType.find(order.request_types.last)
-
multiplier_hash = order.request_options.fetch(:multiplier, {})
-
sequencing_multiplier = (multiplier_hash[sequencing_request.id.to_s]||multiplier_hash.fetch(sequencing_request.id, 1)).to_i
-
-
if order.multiplexed?
-
sequencing_multiplier
-
else
-
order.assets.count * sequencing_multiplier
-
end
-
end
-
1
private :lanes_from_request_options
-
-
1
def lanes_from_request_counting
-
submission.requests.where_is_a?(SequencingRequest).count
-
end
-
1
private :lanes_from_request_counting
-
-
1
def method_missing(name, *args, &block)
-
name_without_assignment = name.to_s.sub(/=$/, '').to_sym
-
return super unless self.attributes.include?(name_without_assignment)
-
-
instance_variable_name = :"@#{name_without_assignment}"
-
return instance_variable_get(instance_variable_name) if name_without_assignment == name.to_sym
-
instance_variable_set(instance_variable_name, args.first)
-
end
-
1
protected :method_missing
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module Submission::Priorities
-
-
1
def self.priorities
-
['None','Low','Medium','High']
-
end
-
-
1
def self.options
-
(0...priorities.count).map do |i|
-
["#{priorities[i]} - #{i}", i]
-
end
-
end
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
validates_presence_of :priority
-
1
validates_numericality_of :priority, {:only_integer => true, :greater_than_or_equal_to => 0, :less_than_or_equal_to => 3}
-
end
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014 Genome Research Ltd.
-
1
module Submission::ProjectValidation
-
1
def self.included(base)
-
1
base.class_eval do
-
# We probably want to move this validation
-
1
validates_each(:project, :if => :checking_project?) do |record, attr, project|
-
record.errors.add(:base,"Project #{project.name} is not approved") unless project.approved?
-
record.errors.add(:base,"Project #{project.name} is not active") unless project.active?
-
record.errors.add(:base,"Project #{project.name} does not have a budget division") unless project.actionable?
-
end
-
-
1
validates_each(:project, :if => :validating?) do |record, attr, project|
-
record.errors.add(:base,"Project #{project.name} is not suitable for submission: #{project.errors.full_messages.join('; ')}") unless project.submittable?
-
end
-
-
1
after_create :confirm_validity!
-
end
-
end
-
-
1
def complete_building
-
check_project_details!
-
super
-
end
-
-
1
def checking_project?
-
validating? && project.enforce_quotas?
-
end
-
-
1
def validating?
-
project && @checking_project
-
end
-
-
1
Error = Class.new(Exception)
-
-
1
def check_project_details!
-
raise Submission::ProjectValidation::Error, self.errors.full_messages.join("\n") unless self.submittable?
-
end
-
1
private :check_project_details!
-
-
1
def multiplier_for(request_type)
-
return 1 if self.request_options.blank? or not self.request_options.key?(:multiplier)
-
self.request_options[:multiplier][request_type.id.to_i] || 1
-
end
-
1
private :multiplier_for
-
-
1
def submittable?
-
begin
-
@checking_project = true
-
valid?
-
ensure
-
@checking_project = false
-
end
-
end
-
-
# Hack to be able to build order
-
# from pulled data
-
1
def save_after_unmarshalling
-
@saving_without_validation=true
-
save_without_validation
-
@saving_without_validation=false
-
end
-
-
-
1
def confirm_validity!
-
return if @saving_without_validation
-
check_project_details!
-
end
-
1
private :confirm_validity!
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Submission::RequestOptionsBehaviour
-
1
def self.included(base)
-
1
base.class_eval do
-
1
serialize :request_options
-
1
validate :check_request_options, :if => :request_options_changed?
-
end
-
end
-
-
1
def check_request_options
-
check_multipliers_are_valid
-
end
-
1
private :check_request_options
-
-
1
def check_multipliers_are_valid
-
multipliers = self.request_options.try(:[], :multiplier)
-
return if multipliers.blank? # We're ok with nothing being specified!
-
-
# TODO[xxx]: should probably error if they've specified a request type that isn't being used
-
errors.add(:request_options, 'negative multiplier supplied') if multipliers.values.map(&:to_i).any? { |v| v < 0 }
-
errors.add(:request_options, 'zero multiplier supplied') if multipliers.values.map(&:to_i).any?(&:zero?)
-
end
-
1
private :check_multipliers_are_valid
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014,2015 Genome Research Ltd.
-
-
1
require 'aasm'
-
-
1
module Submission::StateMachine
-
1
def self.extended(base)
-
1
base.class_eval do
-
1
include AASM
-
1
include InstanceMethods
-
-
1
configure_state_machine
-
1
configure_named_scopes
-
-
1
def editable?
-
state=="building"
-
end
-
-
end
-
end
-
-
1
module InstanceMethods
-
# TODO[xxx]: This should be a guard but what the heck ...
-
1
def left_building_state?
-
not self.building? or !!@leaving_building_state
-
end
-
-
1
def valid_for_leaving_building_state
-
@leaving_building_state = true
-
raise ActiveRecord::RecordInvalid, self unless valid?
-
ensure
-
@leaving_building_state = false
-
end
-
# TODO[xxx]: ... to here
-
-
1
def complete_building
-
orders(true).each(&:complete_building)
-
end
-
-
1
def process_submission!
-
# Does nothing by default!
-
end
-
-
1
def process_callbacks!
-
callbacks.each do |_,callback|
-
callback.call
-
end
-
end
-
-
1
def callbacks
-
@callbacks ||= {}
-
end
-
-
1
def register_callback(key=nil,&block)
-
key ||= "k#{@callbacks.size}"
-
callbacks[key] = block
-
end
-
-
1
def unprocessed?
-
UnprocessedStates.include?(state)
-
end
-
-
1
def cancellable?
-
(pending? || ready?) && requests_cancellable?
-
end
-
-
1
def requests_cancellable?
-
# Default behaviour, overidden in the model itself
-
false
-
end
-
-
1
def broadcast_events
-
orders.each(&:generate_broadcast_event)
-
end
-
end
-
-
1
def configure_state_machine
-
1
aasm_column :state
-
1
aasm_initial_state :building
-
1
aasm_state :building, :exit => :valid_for_leaving_building_state
-
1
aasm_state :pending, :enter => :complete_building
-
1
aasm_state :processing, :enter => :process_submission!, :exit => :process_callbacks!
-
1
aasm_state :ready, :enter => :broadcast_events
-
1
aasm_state :failed
-
1
aasm_state :cancelled, :enter => :cancel_all_requests
-
-
1
aasm_event :built do
-
1
transitions :to => :pending, :from => [ :building ]
-
end
-
-
1
aasm_event :cancel do
-
1
transitions :to => :cancelled, :from => [ :pending, :ready, :cancelled ], :guard => :requests_cancellable?
-
end
-
-
1
aasm_event :process do
-
1
transitions :to => :processing, :from => [:processing, :failed, :pending]
-
end
-
-
1
aasm_event :ready do
-
1
transitions :to => :ready, :from => [:processing, :failed]
-
end
-
-
1
aasm_event :fail do
-
1
transitions :to => :failed, :from => [:processing, :failed, :pending]
-
end
-
end
-
1
private :configure_state_machine
-
-
1
UnprocessedStates = ["building", "pending", "processing"]
-
1
def configure_named_scopes
-
1
scope :unprocessed, -> { where(:state => UnprocessedStates) }
-
1
scope :processed, -> { where(:state => ["ready", "failed"]) }
-
end
-
-
1
private :configure_named_scopes
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
-
1
require 'aasm'
-
-
1
class Submission::SubmissionCreator < Submission::PresenterSkeleton
-
1
SubmissionsCreaterError = Class.new(StandardError)
-
1
IncorrectParamsException = Class.new(SubmissionsCreaterError)
-
1
InvalidInputException = Class.new(SubmissionsCreaterError)
-
-
1
self.attributes = [
-
:id,
-
:template_id,
-
:sample_names_text,
-
:barcodes_wells_text,
-
:study_id,
-
:submission_id,
-
:project_name,
-
:plate_purpose_id,
-
:lanes_of_sequencing_required,
-
:comments,
-
:orders,
-
:order_params,
-
:asset_group_id,
-
:pre_capture_plex_group,
-
:gigabases_expected,
-
:priority
-
]
-
-
-
1
def build_submission!
-
begin
-
submission.built!
-
rescue AASM::InvalidTransition
-
submission.errors.add(:base,"Submissions can not be edited once they are submitted for building.")
-
rescue ActiveRecord::RecordInvalid => exception
-
exception.record.errors.full_messages.each do |message|
-
submission.errors.add(:base,message)
-
end
-
rescue Submission::ProjectValidation::Error => exception
-
submission.errors.add(:base,exception.message)
-
end
-
end
-
-
1
def per_order_settings
-
[:pre_capture_plex_level, :gigabases_expected, :customer_accepts_responsibility]
-
end
-
-
1
def find_asset_group
-
AssetGroup.find(asset_group_id) if asset_group_id.present?
-
end
-
-
# Returns the either the first order associated with the submission or
-
# creates a new blank order.
-
1
def order
-
return @order if @order.present?
-
return submission.orders.first if submission.present?
-
-
@order = create_order
-
end
-
-
1
def cross_compatible?
-
order.cross_compatible?
-
end
-
-
1
def create_order
-
order_role = Order::OrderRole.find_by_role(order_params.delete('order_role')) if order_params.present?
-
new_order = template.new_order(
-
:study => study,
-
:project => project,
-
:user => @user,
-
:request_options => order_params,
-
:comments => comments,
-
:pre_cap_group => pre_capture_plex_group,
-
:order_role => order_role
-
)
-
new_order.request_type_multiplier do |sequencing_request_type_id|
-
new_order.request_options[:multiplier][sequencing_request_type_id] = (lanes_of_sequencing_required || 1)
-
end if order_params
-
-
new_order
-
end
-
1
private :create_order
-
-
1
def order_params
-
@order_params = @order_params.to_hash if @order_params.class == HashWithIndifferentAccess
-
@order_params[:multiplier] = HashWithIndifferentAccess.new if @order_params && @order_params[:multiplier].nil?
-
@order_params
-
end
-
-
1
def order_fields
-
if order.input_field_infos.flatten.empty?
-
order.request_type_ids_list = order.request_types.map { |rt| [rt] }
-
end
-
order.input_field_infos.reject {|info| per_order_settings.include?(info.key)}
-
end
-
-
# Return the submission's orders or a blank array
-
1
def orders
-
return [] unless submission.present?
-
submission.try(:orders).map { |o| OrderPresenter.new(o) }
-
end
-
-
1
def project
-
@project ||= Project.find_by_name(@project_name)
-
end
-
-
# Creates a new submission and adds an initial order on the submission using
-
# the parameters
-
1
def save
-
begin
-
ActiveRecord::Base.transaction do
-
# Add assets to the order...
-
new_order = create_order.tap { |o| o.update_attributes!(order_assets) }
-
-
if submission.present?
-
# The submission should be destroyed if we delete the last order on it so
-
# we shouldn't see any empty submissions.
-
-
submission.orders << new_order
-
else
-
@submission = new_order.create_submission(:user => order.user, :priority=>priority)
-
end
-
-
new_order.save!
-
@order = new_order
-
end
-
-
rescue Submission::ProjectValidation::Error => project_exception
-
order.errors.add(:base,project_exception.message)
-
rescue InvalidInputException => input_exception
-
order.errors.add(:base,input_exception.message)
-
rescue IncorrectParamsException => exception
-
order.errors.add(:base,exception.message)
-
rescue ActiveRecord::RecordInvalid => exception
-
exception.record.errors.full_messages.each do |message|
-
order.errors.add(:base,message)
-
end
-
end
-
-
# Having got through that lot, return whether the save was successful or not
-
order.errors.empty?
-
end
-
-
1
def order_assets
-
input_methods = [ :asset_group_id, :sample_names_text, :barcodes_wells_text ].select { |input_method| send(input_method).present? }
-
-
raise InvalidInputException, "No Samples found" if input_methods.empty?
-
raise InvalidInputException, "Samples cannot be added from multiple sources at the same time." unless input_methods.size == 1
-
-
return case input_methods.first
-
when :asset_group_id then { :asset_group => find_asset_group }
-
when :sample_names_text then
-
{
-
:assets => wells_on_specified_plate_purpose_for(
-
plate_purpose,
-
find_samples_from_text(sample_names_text)
-
)
-
}
-
when :barcodes_wells_text then
-
{
-
:assets => find_assets_from_text(barcodes_wells_text)
-
}
-
-
else raise StandardError, "No way to determine assets for input choice #{input_methods.first}"
-
end
-
end
-
-
# This is a legacy of the old controller...
-
1
def wells_on_specified_plate_purpose_for(plate_purpose, samples)
-
samples.map do |sample|
-
sample.wells.all(:include => :plate).detect { |well| well.plate.present? and (well.plate.plate_purpose_id == plate_purpose.id) } or
-
raise InvalidInputException, "No #{plate_purpose.name} plate found with sample: #{sample.name}"
-
end
-
end
-
-
1
def cross_project
-
false
-
end
-
-
1
def cross_study
-
false
-
end
-
-
1
def plate_purpose
-
@plate_purpose ||= PlatePurpose.find(plate_purpose_id)
-
end
-
-
# Returns Samples based on Sample name or Sanger ID
-
# This is a legacy of the old controller...
-
1
def find_samples_from_text(sample_text)
-
names = sample_text.lines.map(&:chomp).reject(&:blank?).map(&:strip)
-
-
samples = Sample.all(
-
:include => :assets,
-
:conditions => [ 'name IN (:names) OR sanger_sample_id IN (:names)', { :names => names } ]
-
)
-
-
name_set = Set.new(names)
-
found_set = Set.new(samples.map { |s| [ s.name, s.sanger_sample_id ] }.flatten)
-
not_found = name_set - found_set
-
raise InvalidInputException, "#{Sample.table_name} #{not_found.to_a.join(", ")} not found" unless not_found.empty?
-
return samples
-
end
-
1
private :find_samples_from_text
-
-
1
def find_assets_from_text(assets_text)
-
plates_wells = assets_text.lines.map(&:chomp).reject(&:blank?).map(&:strip)
-
-
plates_wells.map do |plate_wells|
-
plate_barcode, well_locations = plate_wells.split(':')
-
begin
-
plate = Plate.find_from_machine_barcode(Barcode.human_to_machine_barcode(plate_barcode))
-
rescue Barcode::InvalidBarcode => exception
-
raise InvalidInputException, "Invalid Barcode #{plate_barcode}: #{exception}"
-
end
-
raise InvalidInputException, "No plate found for barcode #{plate_barcode}." if plate.nil?
-
well_array = (well_locations||'').split(',').reject(&:blank?).map(&:strip)
-
-
find_wells_in_array(plate,well_array)
-
end.flatten
-
end
-
1
private :find_assets_from_text
-
-
1
def find_wells_in_array(plate,well_array)
-
return plate.wells.with_aliquots.select('DISTINCT assets.*').all if well_array.empty?
-
well_array.map do |map_description|
-
case map_description
-
when /^[a-z,A-Z][0-9]+$/ # A well
-
well = plate.find_well_by_name(map_description)
-
if well.nil? or well.aliquots.empty?
-
raise InvalidInputException, "Well #{map_description} on #{plate.sanger_human_barcode} does not exist or is empty."
-
else
-
well
-
end
-
when /^[a-z,A-Z]$/ # A row
-
plate.wells.with_aliquots.in_plate_row(map_description,plate.size).select('DISTINCT assets.*').all
-
when /^[0-9]+$/ # A column
-
plate.wells.with_aliquots.in_plate_column(map_description,plate.size).select('DISTINCT assets.*').all
-
else
-
raise InvalidInputException, "#{map_description} is not a valid well location"
-
end
-
end
-
end
-
1
private :find_wells_in_array
-
-
1
def study
-
@study ||= (Study.find(@study_id) if @study_id.present?)
-
end
-
-
1
def studies
-
@studies ||= [ study ] if study.present?
-
@studies ||= @user.interesting_studies.sort {|a,b| a.name <=> b.name }
-
end
-
-
1
def submission
-
return nil unless id.present? || @submission
-
@submission ||= Submission.find(id)
-
end
-
-
# Returns the SubmissionTemplate (OrderTemplate) to be used for this Submission.
-
1
def template
-
# We can't get the template from a saved order, have to find by name.... :(
-
@template = SubmissionTemplate.find_by_name(order.template_name) if try(:submission).try(:orders).present?
-
@template ||= SubmissionTemplate.find(@template_id)
-
end
-
-
1
def templates
-
@templates ||= SubmissionTemplate.visible.include_product_line
-
end
-
-
1
def product_lines
-
templates.group_by {|t| t.product_line.try(:name)||'General' }
-
end
-
-
1
def template_id
-
submission.try(:orders).try(:first).try(:id)
-
end
-
-
# Returns an array of all the names of active projects associated with the
-
# current user.
-
1
def user_valid_projects
-
@user_active_projects ||= @user.valid_projects
-
end
-
-
1
def url(view)
-
view.send(:submission_path, submission.present? ? submission : { :id => 'DUMMY_ID' })
-
end
-
-
1
def template_name
-
submission.orders.first.template_name
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
1
class Submission::SubmissionPresenter < Submission::PresenterSkeleton
-
1
self.attributes = [ :id ]
-
-
1
def submission
-
@submission ||= Submission.find(id)
-
end
-
-
1
def priority
-
submission.priority
-
end
-
-
1
def template_name
-
submission.orders.first.template_name
-
end
-
-
1
def order
-
submission.orders.first
-
end
-
-
# Deleting a Submission should also delete all associated Orders.
-
1
def destroy
-
submission.orders.destroy_all
-
submission.destroy
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class Submission::Workflow < ActiveRecord::Base
-
1
has_many :request_types
-
1
has_many :orders
-
1
has_many :items
-
-
1
def self.default_workflow
-
self.find_by_name('Next-gen sequencing') or raise StandardError, "Cannot find submission workflow 'Next-gen sequencing'"
-
end
-
-
1
FIELDS_TO_WORKFLOWS = {
-
'Microarray genotyping' => [
-
# Project metadata fields
-
'project.metadata.gt_committee_tracking_id'
-
],
-
'Next-gen sequencing' => [
-
# Project metadata fields
-
'project.metadata.project_manager_id',
-
'project.metadata.funding_comments',
-
'project.metadata.collaborators',
-
'project.metadata.external_funding_source'
-
]
-
5
}.inject(Hash.new { |h,k| h[k] = [] }) do |fields_to_workflows,(workflow, acceptable_fields)|
-
2
fields_to_workflows.tap do
-
2
acceptable_fields.each do |field|
-
5
fields_to_workflows[field] << workflow
-
end
-
end
-
end
-
-
1
def visible_attribute?(field_path)
-
workflows_accepting_field = FIELDS_TO_WORKFLOWS[field_path.join('.')]
-
return (workflows_accepting_field.blank? || workflows_accepting_field.include?(self.name))
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# SubmissionPools are designed to view submissions in the context of a particular asset
-
1
class SubmissionPool < ActiveRecord::Base
-
-
1
module Association
-
1
module Plate
-
1
def self.included(base)
-
1
base.class_eval do
-
-
# Rails 4 takes scopes as second argument, we can probablu also tidy up and remove the counter_sql
-
# as it is the :group by seems to throw rails, and distinct will throw off out count.
-
1
has_many :submission_pools, :through => :active_requests,
-
:select => 'submissions.*, requests.id AS outer_request_id',
-
:group => 'submissions.id', :uniq => true do
-
-
1
def count(*args)
-
# Horrid hack due to the behaviour of count with a group_by
-
# We can't use uniq alone, as the outer_request_id makes
-
# the vairous rows unique.
-
s = super
-
return s if s.is_a?(Numeric)
-
s.length
-
end
-
end
-
-
1
has_many :active_requests, :conditions => {
-
:state => Request::Statemachine::ACTIVE,
-
:request_purpose_id => RequestPurpose.standard
-
},
-
:through => :wells, :source => :requests
-
-
end
-
end
-
-
end
-
end
-
-
1
self.table_name = 'submissions'
-
-
1
belongs_to :outer_request, :class_name => 'Request'
-
1
has_many :tag2_layout_template_submissions, :class_name => 'Tag2Layout::TemplateSubmission', :foreign_key => 'submission_id'
-
1
has_many :tag2_layout_templates, :through => :tag2_layout_template_submissions
-
-
1
scope :include_uuid, ->() { }
-
1
scope :include_outer_request, ->() { includes(:outer_request) }
-
-
1
scope :for_plate, ->(plate) {
-
-
stock_plate = plate.stock_plate
-
-
return where('false') if stock_plate.nil?
-
-
select('submissions.*, our.id AS outer_request_id').
-
joins([
-
'LEFT JOIN requests as our ON our.submission_id = submissions.id',
-
'LEFT JOIN container_associations as spw ON spw.content_id = our.asset_id'
-
]).
-
where([
-
'spw.container_id =? AND our.sti_type NOT IN (?) AND our.state IN (?)',
-
stock_plate.id,
-
[TransferRequest,*TransferRequest.descendants].map(&:name),
-
Request::Statemachine::ACTIVE
-
]).
-
group('submission_id')
-
}
-
-
1
def plates_in_submission
-
outer_request.submission_plate_count
-
end
-
-
1
def used_tag2_layout_templates
-
tag2_layout_templates.map {|template| {"uuid"=>template.uuid,"name"=>template.name}}
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
# It associates a name to a pre-filled submission (subclass) and a serialized set of attributes
-
# We could have use a Prototype Factory , and so just associate a name to existing submission
-
# but that doesn't work because the submission prototype doesn't pass the validation stage.
-
# Anyway that's basically a prototype factory
-
class SubmissionTemplate < ActiveRecord::Base
-
include Uuid::Uuidable
-
-
validates_presence_of :name
-
validates_presence_of :submission_class_name
-
-
serialize :submission_parameters
-
-
-
has_many :orders
-
belongs_to :product_line
-
-
has_many :supercedes, :class_name => 'SubmissionTemplate', :foreign_key => :superceded_by_id
-
belongs_to :superceded_by, :class_name => 'SubmissionTemplate', :foreign_key => :superceded_by_id
-
-
1
belongs_to :product_catalogue, :inverse_of => :submission_templates
-
delegate :product_for, :to => :product_catalogue
-
1
validates_presence_of :product_catalogue
-
-
1
LATEST_VERSION = -1
-
1
SUPERCEDED_BY_UNKNOWN_TEMPLATE = -2
-
-
1
scope :hidden, -> { order('product_line_id ASC').where([ 'superceded_by_id != ?', LATEST_VERSION ]) }
-
1
scope :visible, -> { order('product_line_id ASC').where( :superceded_by_id => LATEST_VERSION ) }
-
1
scope :include_product_line, -> { includes(:product_line) }
-
-
1
def visible
-
self.superceded_by_id == LATEST_VERSION
-
end
-
-
1
def superceded_by_unknown!
-
self.superceded_by_id = SUPERCEDED_BY_UNKNOWN_TEMPLATE
-
end
-
-
1
def supercede(&block)
-
ActiveRecord::Base.transaction do
-
self.dup.tap do |cloned|
-
yield(cloned) if block_given?
-
name, cloned.name = cloned.name, "Superceding #{cloned.name}"
-
cloned.save!
-
self.update_attributes!(:superceded_by_id => cloned.id, :superceded_at => Time.now)
-
cloned.update_attributes!(:name => name)
-
end
-
end
-
end
-
-
1
def create_and_build_submission!(attributes)
-
Submission.build!(attributes.merge(:template => self))
-
end
-
1
def create_order!(attributes)
-
self.new_order(attributes).tap do |order|
-
yield(order) if block_given?
-
order.save!
-
end
-
end
-
-
1
def create_with_submission!(attributes)
-
self.create_order!(attributes) do |order|
-
order.create_submission(:user_id => order.user_id)
-
end
-
end
-
-
# create a new submission of the good subclass and with pre-set attributes
-
1
def new_order(params={})
-
duped_params = safely_duplicate(params)
-
# NOTE: Stringifying request_option keys here is NOT a good idea as it affects multipliers
-
attributes = submission_attributes.with_indifferent_access.deep_merge(duped_params)
-
infos = SubmissionTemplate.unserialize(attributes.delete(:input_field_infos))
-
-
submission_class.new(attributes).tap do |order|
-
order.template_name = self.name
-
order.product = product_for(params)
-
order.set_input_field_infos(infos) unless infos.nil?
-
end
-
end
-
-
# TODO[xxx]: This is a hack just so I can move forward but the request_types stuff should come directly
-
1
def submission_attributes
-
return {} if self.submission_parameters.nil?
-
-
submission_attributes = Marshal.load(Marshal.dump(self.submission_parameters)) # Deep clone
-
submission_attributes[:request_types] = submission_attributes[:request_type_ids_list].flatten
-
submission_attributes
-
end
-
1
private :submission_attributes
-
-
# Takes in the parameters passed for the order and safely duplicates it so that it can be modified
-
# without affecting the caller version.
-
#
-
# NOTE: You cannot use Marshal.load(Marshal.dump(params)) here because it causes all kinds of problems with
-
# the ActiveRecord::Base derived classes when params contains their instances. It'll appear as insecure
-
# method errors somewhere else in the code.
-
1
def safely_duplicate(params)
-
params.inject({}) do |cloned, (k,v)|
-
if v.is_a?(ActiveRecord::Base)
-
cloned[k] = v
-
elsif v.is_a?(Array) and v.first.is_a?(ActiveRecord::Base)
-
cloned[k] = v.dup # Duplicate the array, but not the contents
-
elsif v.is_a?(Array) or v.is_a?(Hash)
-
cloned[k] = Marshal.load(Marshal.dump(v)) # Make safe copies of arrays and hashes
-
else
-
cloned[k] = v
-
end
-
cloned
-
end
-
end
-
1
private :safely_duplicate
-
-
# create a new template from a submission
-
1
def self.new_from_submission(name, submission)
-
submission_template = new(:name => name)
-
submission_template.update_from_submission(submission)
-
return submission_template
-
end
-
-
1
def update_from_submission(submission)
-
self.submission_class_name = submission.class.name
-
self.submission_parameters = submission.template_parameters
-
end
-
-
1
def submission_class
-
klass = submission_class_name.constantize
-
#TODO[mb14] Hack. This is to avoid to have to rename it in database or seen
-
#The hack is not needed for subclasses as they inherits from Order
-
klass == Submission ? Order : klass
-
end
-
-
1
private
-
-
1
def self.unserialize(object)
-
if object.respond_to? :map
-
return object.map { |o| unserialize(o) }
-
else
-
return object
-
end
-
end
-
end
-
-
# SubmissionTemplate is really OrderTemplate, and the only place that actually cares is the API, so alias
-
1
OrderTemplate = SubmissionTemplate
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class SubmissionTemplateRequestType < ActiveRecord::Base
-
1
belongs_to :submission_template
-
1
belongs_to :request_type
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class SubmittedAsset < ActiveRecord::Base
-
1
belongs_to :order
-
1
belongs_to :asset
-
-
1
validates_presence_of :order
-
1
validates_presence_of :asset
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2014 Genome Research Ltd.
-
1
class Supplier < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include ::Io::Supplier::ApiIoSupport
-
1
include SampleManifest::Associations
-
-
1
has_many :studies, :through => :sample_manifests, :uniq => true
-
1
validates_presence_of :name
-
-
-
# Named scope for search by query string behaviour
-
1
scope :for_search_query, ->(query,with_includes) {
-
where(['suppliers.name IS NOT NULL AND (suppliers.name LIKE :like)', { :like => "%#{query}%", :query => query }])
-
}
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014 Genome Research Ltd.
-
1
module Swipecardable
-
-
1
def swipecard_code=(code)
-
self.encrypted_swipecard_code= User.encrypt_swipecard_code(code)
-
end
-
-
1
def swipecard_code
-
end
-
-
1
def swipecard_code?
-
encrypted_swipecard_code?
-
end
-
-
-
1
def compare_swipecard_code(code)
-
User.encrypt_swipecard_code(code) == encrypted_swipecard_code
-
end
-
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
def self.encrypt_swipecard_code(code)
-
User.encrypt(code, nil)
-
end
-
# won't work, because of the salt.
-
1
scope :with_swipecard_code, ->(*swipecard_codes) { { :conditions => { :encrypted_swipecard_code => swipecard_codes.flatten.map { |sw| encrypt_swipecard_code(sw) } } } }
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# A class used for internal requests that drive sequencescape operation.
-
1
class SystemRequest < Request
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Tag < ActiveRecord::Base
-
1
module Associations
-
1
def untag!
-
aliquots.first.try(:update_attributes!, :tag => nil)
-
end
-
end
-
-
1
include Api::TagIO::Extensions
-
-
1
self.per_page = 500
-
1
include Uuid::Uuidable
-
-
-
-
1
belongs_to :tag_group
-
1
has_many :assets, :as => :material
-
1
has_many :requests, :through => :assets, :uniq => true
-
-
1
scope :sorted , order("map_id ASC")
-
-
1
def name
-
"Tag #{map_id}"
-
end
-
-
# Creates an instance of this tag that can be attached to a well.
-
1
def create!
-
TagInstance.create!(:tag => self)
-
end
-
1
deprecate :create!
-
-
# Connects a tag instance to the specified asset
-
1
def tag!(asset)
-
raise StandardError, "Cannot tag an empty asset" if asset.aliquots.empty?
-
raise StandardError, "Cannot tag multiple samples" if asset.aliquots.size > 1
-
asset.aliquots.first.update_attributes!(:tag => self)
-
end
-
-
# Map id is converted to a string here for consistency with elsewhere in the api.
-
1
def summary
-
{
-
:tag_group => tag_group.name,
-
:tag_index => map_id.to_s
-
}
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
# Lays out the tags in the specified tag group in a particular pattern.
-
#
-
# Applies a single tag 2 to the entire plate
-
1
class Tag2Layout < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
-
1
attr_writer :layout_template
-
##
-
# This class provides two benefits
-
# 1) We can enforce uniqueness of tag2_layouts/submissions at the database level
-
# This helps avoid potential race conditions (Although they won't be handled
-
# especially elegantly)
-
# 2) It provides an easy means of looking up used templates
-
1
class TemplateSubmission < ActiveRecord::Base
-
1
belongs_to :submission
-
1
belongs_to :tag2_layout_template
-
1
validates_presence_of :tag2_layout_template_id, :submission_id
-
1
validates_uniqueness_of :tag2_layout_template_id, :scope => :submission_id
-
end
-
-
# The user performing the layout
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
# The tag group to layout on the plate, along with the substitutions that should be made
-
1
belongs_to :tag
-
1
validates_presence_of :tag
-
-
1
serialize :substitutions
-
-
1
belongs_to :plate
-
1
validates_presence_of :plate
-
-
1
belongs_to :source, :class_name => 'Asset'
-
-
1
scope :include_tag, ->() { includes(:tag) }
-
1
scope :include_plate, ->() { includes(:plate) }
-
-
1
before_create :record_template_use
-
# After creating the instance we can layout the tags into the wells.
-
1
after_create :layout_tag2_into_wells, :if => :valid?
-
-
1
def record_template_use
-
plate.submissions.each do |submission|
-
TemplateSubmission.create!(:submission=>submission,:tag2_layout_template=>layout_template)
-
end
-
end
-
-
1
def layout_tag2_into_wells
-
plate.wells.include_aliquots.each {|w| w.assign_tag2(tag) }
-
end
-
-
1
def layout_template
-
@layout_template||Tag2LayoutTemplate.find_by_tag_id(tag)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
# Tag 2 Layouts apply a single tag to the entire plate
-
1
class Tag2LayoutTemplate < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include Lot::Template
-
-
1
belongs_to :tag
-
1
validates_presence_of :tag
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name
-
-
1
scope :include_tag, ->() { includes(:tag) }
-
-
# Create a TagLayout instance that does the actual work of laying out the tags.
-
1
def create!(attributes = {}, &block)
-
Tag2Layout.create!(attributes.merge(default_attributes),&block)
-
end
-
-
1
def stamp_to(_)
-
# Do Nothing
-
end
-
-
1
private
-
-
1
def default_attributes
-
{:tag=>tag,:layout_template=>self}
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class TagGroup < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
-
1
has_many :tags, :order => 'map_id ASC'
-
-
1
scope :include_tags, ->() { includes(:tags) }
-
-
-
1
scope :visible, -> { where(:visible => true) }
-
-
1
validates_presence_of :name
-
1
validates_uniqueness_of :name
-
-
1
def create_tags(tags_properties)
-
return if tags_properties.blank?
-
tags_properties.each do |index,tag_properties|
-
next if tag_properties[:oligo].blank?
-
self.tags << Tag.create(tag_properties)
-
end
-
end
-
-
1
def tags_sorted_by_map_id
-
self.tags.sort_by(&:map_id)
-
end
-
-
# Returns a Hash that maps from the tag index in the group to the oligo sequence for the tag
-
1
def indexed_tags
-
Hash[tags.map { |tag| [ tag.map_id, tag.oligo ] }]
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class TagGroupsTask < Task
-
-
1
class TagGroupsData < Task::RenderElement
-
1
alias_attribute :well, :asset
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && TagGroupsData.new(request)
-
end
-
-
1
def partial
-
"tag_groups_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_tag_groups_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014 Genome Research Ltd.
-
# Lays out the tags in the specified tag group in a particular pattern.
-
#
-
# In pulldown they use only one set of tags and put them into wells in a particular pattern: by columns, or
-
# by rows. Depending on the size of the tag group that is used by the layout template it either repeats (for
-
# example, 8 tags in the group laid out in columns would repeat the tags across the plate), or it
-
# doesn't (for example, a 96 tag group would occupy an entire 96 well plate).
-
1
class TagLayout < ActiveRecord::Base
-
1
include Uuid::Uuidable
-
1
include ModelExtensions::TagLayout
-
-
1
self.inheritance_column = "sti_type"
-
-
# The user performing the layout
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
# The tag group to layout on the plate, along with the substitutions that should be made
-
1
belongs_to :tag_group
-
1
validates_presence_of :tag_group
-
1
serialize :substitutions
-
-
1
validates_presence_of :direction_algorithm
-
1
validates_presence_of :walking_algorithm
-
-
1
before_validation do |record|
-
record.substitutions ||= {}
-
end
-
-
# The plate we'll be laying out the tags into
-
1
belongs_to :plate
-
1
validates_presence_of :plate
-
-
1
include Asset::Ownership::ChangesOwner
-
1
set_target_for_owner(:plate)
-
-
# After loading the record from the database, inject the behaviour.
-
1
after_initialize :import_behaviour
-
-
1
def import_behaviour
-
extend(direction_algorithm.constantize) unless direction_algorithm.blank?
-
extend(walking_algorithm.constantize) unless walking_algorithm.blank?
-
end
-
-
1
def direction=(new_direction)
-
self.direction_algorithm = {
-
'column' => 'TagLayout::InColumns',
-
'row' => 'TagLayout::InRows',
-
'inverse column' => 'TagLayout::InInverseColumns',
-
'inverse row' => 'TagLayout::InInverseRows'
-
}[new_direction]
-
errors.add(:base,"#{new_direction} is not a valid direction")if self.direction_algorithm.nil?
-
raise(ActiveRecord::RecordInvalid, self) if self.direction_algorithm.nil?
-
extend(direction_algorithm.constantize)
-
end
-
-
1
def walking_by=(walk)
-
self.walking_algorithm = {
-
'wells in pools' => 'TagLayout::WalkWellsByPools',
-
'wells of plate' => 'TagLayout::WalkWellsOfPlate',
-
'manual by pool' => 'TagLayout::WalkManualWellsByPools',
-
'manual by plate' => 'TagLayout::WalkManualWellsOfPlate'
-
}[walk]
-
errors.add(:base,"#{walk} is not a recognised walking method") if self.walking_algorithm.nil?
-
raise(ActiveRecord::RecordInvalid, self) if self.walking_algorithm.nil?
-
extend(walking_algorithm.constantize)
-
end
-
-
-
1
def wells_in_walking_order
-
plate.wells.send(:"in_#{direction.gsub(' ', '_')}_major_order")
-
end
-
1
private :wells_in_walking_order
-
-
# After creating the instance we can layout the tags into the wells.
-
1
after_create :layout_tags_into_wells, :if => :valid?
-
-
# Convenience mechanism for laying out tags in a particular fashion.
-
1
def layout_tags_into_wells
-
# Make sure that the substitutions requested by the user are handled before applying the tags
-
# to the wells.
-
-
tag_map_id_to_tag = ActiveSupport::OrderedHash[tag_group.tags.sort_by(&:map_id).map { |tag| [tag.map_id.to_s, tag] }]
-
tags = tag_map_id_to_tag.map { |k,tag| substitutions.key?(k) ? tag_map_id_to_tag[substitutions[k]] : tag }
-
walk_wells do |well, index|
-
tags[(index+initial_tag) % tags.length].tag!(well) unless well.aliquots.empty?
-
end
-
-
# We can now check that the pools do not contain duplicate tags.
-
pool_to_tag = Hash.new { |h,k| h[k] = [] }
-
plate.wells.walk_in_pools do |pool_id, wells|
-
pool_to_tag[pool_id] = wells.map { |well| well.aliquots.map(&:tag).uniq }.flatten
-
end
-
errors.add(:base,'duplicate tags within a pool') if pool_to_tag.any? { |_,t| t.uniq.size > 1 }
-
end
-
1
private :layout_tags_into_wells
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# Lays out the tags so that they are based on the pool.
-
1
class TagLayout::ByPools < TagLayout
-
# The direction of the tagging is column major, within the pools.
-
1
class_attribute :direction, :instance_writer => false
-
1
self.direction = 'column'
-
-
1
def walk_wells(&block)
-
# Take the pools for this plate and flatten them out. This ensures that the wells within a pool are
-
# sequentially stored, and that the pools themselves are sequentially held. Then we can replace each
-
# of the well locations with the actual well that should be part of the pool.
-
well_locations = plate.pools.values.flatten
-
plate.wells.walk_in_column_major_order do |well, _|
-
index = well_locations.index(well.map.description) or next
-
well_locations[index] = well
-
end
-
-
# Finally the application of the tag is simply made by walking the flatten pools.
-
well_locations.each_with_index(&block)
-
end
-
1
private :walk_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
# Lays out the tags so that they are column ordered.
-
1
module TagLayout::InColumns
-
1
extend self
-
-
1
def direction
-
'column'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
# Lays out the tags so that they are inverse column ordered.
-
1
module TagLayout::InInverseColumns
-
1
extend self
-
-
1
def direction
-
'inverse column'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
# Lays out the tags so that they are inverse row ordered.
-
1
module TagLayout::InInverseRows
-
1
extend self
-
-
1
def direction
-
'inverse row'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
# Lays out the tags so that they are row ordered.
-
1
module TagLayout::InRows
-
1
extend self
-
-
1
def direction
-
'row'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
module TagLayout::WalkManualWellsByPools
-
1
def self.walking_by
-
'wells in pools'
-
end
-
-
1
def walking_by
-
TagLayout::WalkManualWellsByPools.walking_by
-
end
-
-
1
def walk_wells(&block)
-
# This is much simple than the automated method
-
wells_in_pools = wells_in_walking_order.with_pool_id.group_by(&:pool_id)
-
-
# Now we can walk the wells in the groups, skipping any that have been nil'd by the above code.
-
wells_in_pools.each do |pool,wells|
-
wells.each_with_index { |(well, _), index| yield(well, index) unless well.nil? }
-
end
-
end
-
1
private :walk_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
1
module TagLayout::WalkManualWellsOfPlate
-
1
def self.walking_by
-
'wells of plate'
-
end
-
-
1
def walking_by
-
TagLayout::WalkManualWellsOfPlate.walking_by
-
end
-
-
1
def walk_wells(&block)
-
wells_in_walking_order.with_aliquots.each_with_index do |well, index|
-
yield(well, index) unless well.nil?
-
end
-
end
-
1
private :walk_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
module TagLayout::WalkWellsByPools
-
1
def self.walking_by
-
'wells in pools'
-
end
-
-
1
def walking_by
-
TagLayout::WalkWellsByPools.walking_by
-
end
-
-
1
def walk_wells(&block)
-
# Adjust each of the groups so that any wells that are in the same pool as those at the same position
-
# in the group to the left are moved to a non-clashing position. Effectively this makes the view of the
-
# plate slightly jagged.
-
group_size = direction.to_sym == :column ? Map::Coordinate.plate_length(plate.size) : Map::Coordinate.plate_width(plate.size)
-
wells_in_groups = wells_in_walking_order.with_pool_id.in_groups_of(group_size).map do |wells|
-
wells.map { |well| [ well, well.pool_id ] }
-
end
-
wells_in_groups.each_with_index do |current_group, group|
-
next if group == 0
-
prior_group = wells_in_groups[group-1]
-
-
current_group.each_with_index do |well_and_pool, index|
-
break if prior_group.size <= index
-
-
# Assume that, if the current well isn't in a pool, that it is in the same pool as the well prior
-
# to it in the group. That way empty wells are treated as though they are part of the pool.
-
well_and_pool[-1] ||= (index.zero? ? prior_group.last : current_group[index-1]).last
-
next unless prior_group[index].last == well_and_pool.last
-
-
current_group.push(well_and_pool) # Move the well to the end of the group
-
current_group[index] = [nil, well_and_pool.last] # Blank out the well at the current position but maintain the pool
-
end
-
end
-
-
# Now we can walk the wells in the groups, skipping any that have been nil'd by the above code.
-
wells_in_groups.each do |group|
-
group.each_with_index { |(well, _), index| yield(well, index) unless well.nil? }
-
end
-
end
-
1
private :walk_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
module TagLayout::WalkWellsOfPlate
-
1
def self.walking_by
-
'wells of plate'
-
end
-
-
1
def walking_by
-
TagLayout::WalkWellsOfPlate.walking_by
-
end
-
-
1
def walk_wells(&block)
-
wells_in_walking_order.each_with_index do |well, index|
-
yield(well, index) unless well.nil?
-
end
-
end
-
1
private :walk_wells
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2014,2015 Genome Research Ltd.
-
# This is a layout template for tags. Think of it as a partially created TagLayout, defining only the tag
-
# group that will be used and the actual TagLayout implementation that will do the work.
-
class TagLayoutTemplate < ActiveRecord::Base
-
include Uuid::Uuidable
-
include Lot::Template
-
-
belongs_to :tag_group
-
validates_presence_of :tag_group
-
-
validates_presence_of :name
-
validates_uniqueness_of :name
-
-
validates_presence_of :direction_algorithm
-
validates_presence_of :walking_algorithm
-
-
1
delegate :direction, :to => :direction_algorithm_class
-
delegate :walking_by, :to => :walking_algorithm_class
-
-
1
scope :include_tags, -> { includes({ :tag_group => :tags }) }
-
-
1
def stamp_to(_)
-
# Do Nothing
-
end
-
-
1
def direction_algorithm_class
-
direction_algorithm.constantize
-
end
-
1
private :direction_algorithm_class
-
-
1
def walking_algorithm_class
-
walking_algorithm.constantize
-
end
-
1
private :walking_algorithm_class
-
-
1
def tag_layout_attributes
-
{
-
:tag_group => tag_group,
-
:direction_algorithm => direction_algorithm,
-
:walking_algorithm => walking_algorithm
-
}
-
end
-
1
private :tag_layout_attributes
-
-
# Create a TagLayout instance that does the actual work of laying out the tags.
-
1
def create!(attributes = {}, &block)
-
TagLayout.create!(attributes.merge(tag_layout_attributes), &block)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Task < ActiveRecord::Base
-
1
belongs_to :workflow, :class_name => "LabInterface::Workflow", :foreign_key => :pipeline_workflow_id
-
1
has_many :families
-
1
has_many :descriptors, :class_name => "Descriptor", :dependent => :destroy
-
-
1
acts_as_descriptable :active
-
-
1
self.inheritance_column = "sti_type"
-
-
-
# BEGIN descriptor_to_attribute, could be move into a mixin
-
-
#TODO move into SetDescriptorsTask
-
1
def get_descriptor_value(name, default=nil)
-
name_s = name.to_s
-
self.descriptors.each do |desc|
-
if desc.name.eql?(name_s)
-
return desc.value
-
end
-
end
-
return default
-
end
-
-
1
def set_descriptor_value(name, value, kind=nil)
-
name_s = name.to_s
-
self.descriptors.each do |desc|
-
if desc.name.eql?(name_s)
-
desc.value = value
-
return
-
end
-
end
-
self.descriptors << Descriptor.new(:name => name_s, :value => value)
-
# self.descriptors.save
-
end
-
# END descriptors
-
-
# BEGIN subclass_to_attribute, could be move into a mixin
-
1
has_many :subclass_attributes, :as => :attributable, :dependent => :destroy, :autosave => true
-
1
def get_subclass_attribute_value(name, default=nil)
-
name_s = name.to_s
-
self.subclass_attributes.each do |desc|
-
if desc.name.eql?(name_s)
-
return desc.value
-
end
-
end
-
return default
-
end
-
-
1
def set_subclass_attribute_value(name, value, kind=nil)
-
name_s = name.to_s
-
self.subclass_attributes.each do |desc|
-
if desc.name.eql?(name_s)
-
desc.value = value
-
return
-
end
-
end
-
self.subclass_attributes << SubclassAttribute.new(:name => name_s, :value => value)
-
# self.subclass.save
-
end
-
-
1
def self.init_class
-
return if @init_done
-
2
@init_done = true
-
1
@subclass_attributes = {}
-
1
@subclass_attributes_ordered_names = []
-
end
-
-
1
def self.get_subclass_attribute_options(name)
-
init_class
-
@subclass_attributes[name]
-
end
-
-
1
def get_subclass_attribute_options(name)
-
self.class.get_subclass_attribute_options(name)
-
end
-
-
1
def self.get_subclass_attributes
-
init_class
-
@subclass_attributes_ordered_names
-
end
-
-
1
def get_subclass_attributes
-
self.class.get_subclass_attributes
-
end
-
-
-
1
def self.set_subclass_attribute(name, options = {})
-
2
init_class
-
2
raise ArgumentError, "subclass attribute #{name} already in use" if @subclass_attributes.include? name
-
-
2
@subclass_attributes[name] = options
-
2
@subclass_attributes_ordered_names << name
-
-
2
kind = options[:kind]
-
2
cast = options[:cast]
-
2
default_value = options[:default]
-
-
2
define_method(name) do
-
value = get_subclass_attribute_value name, default_value # we love closure :)
-
value and case cast
-
when :int
-
value.to_i
-
else
-
value
-
end
-
end
-
-
2
define_method("#{name}=") do |value|
-
set_subclass_attribute_value(name, value, kind)
-
end
-
end
-
-
# END of subclass_to_attiribuet
-
-
-
1
class RenderElement
-
1
attr_reader :request, :asset
-
1
def initialize(request)
-
@request = request
-
@asset = request.asset
-
end
-
end
-
-
1
def partial
-
end
-
-
1
def included_for_do_task
-
[:requests, :pipeline, :lab_events]
-
end
-
-
1
def included_for_render_task
-
[:requests, :pipeline, :lab_events]
-
end
-
-
-
1
def render_task(controller, params)
-
controller.render_task(self, params)
-
end
-
-
1
def create_render_element(request)
-
request && RenderElement.new(request)
-
end
-
-
1
def do_task(controller, params)
-
raise NotImplementedError, "Please Implement a do_task for #{self.class.name}"
-
end
-
-
1
def subassets_for_asset(asset)
-
return [] unless asset
-
sub_assets = []
-
family_map = families.index_by(&:name)
-
return asset.children.select { |a| family_map[a.sti_type] }
-
end
-
-
1
def sub_events_for(event)
-
return []
-
end
-
-
1
def generate_events_from_descriptors(asset)
-
event = LabEvent.new(:description => asset.sti_type)
-
asset.descriptors.each do |descriptor|
-
event.add_descriptor(descriptor) if descriptor.name != "family_id"
-
end
-
return event
-
end
-
-
1
def find_batch(batch_id)
-
Batch.find(batch_id, :include => [:requests, :pipeline, :lab_events])
-
end
-
-
1
def find_batch_requests(batch_id)
-
find_batch(batch_id).ordered_requests
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Tasks::AddSpikedInControlHandler
-
1
def do_add_spiked_in_control_task(task, params)
-
batch = @batch || Batch.find(params[:batch_id])
-
barcode = params[:barcode].first
-
control = SpikedBuffer.find_from_machine_barcode(barcode)
-
request_id_set = Set.new
-
params[:request].each do |k, v|
-
request_id_set << k.to_i if v == "on"
-
end
-
-
unless control
-
flash[:error] = "Can't find a spiked hybridization buffer with barcode #{barcode}"
-
return false
-
end
-
-
Batch.transaction do
-
task.add_control(batch, control, request_id_set)
-
eventify_batch(batch, task)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Tasks::AssignTagsHandler
-
1
def render_assign_tags_task(task, params)
-
@tag_group = TagGroup.find(params[:tag_group])
-
@requests = @batch.requests
-
@tags = @tag_group.tags.sorted
-
@rits = @batch.pipeline.request_information_types
-
end
-
-
1
def do_assign_tags_task(task, params)
-
if params[:mx_library_name].blank?
-
flash[:warning] = "Multiplexed library needs a name"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return false
-
end
-
if MultiplexedLibraryTube.find_all_by_name(params[:mx_library_name]).size > 0
-
flash[:warning] = "Name already in use."
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return false
-
end
-
-
@tag_group = TagGroup.find(params[:tag_group])
-
-
ActiveRecord::Base.transaction do
-
multiplexed_library = Tube::Purpose.standard_mx_tube.create!(:name => params[:mx_library_name], :barcode => AssetBarcode.new_barcode)
-
@batch.requests.each do |request|
-
tag_id = params[:tag][request.id.to_s] or next
-
tag = @tag_group.tags.find(tag_id)
-
tag.tag!(request.target_asset)
-
-
AssetLink.create_edge(request.target_asset, multiplexed_library)
-
RequestType.transfer.create!(:asset => request.target_asset, :target_asset => multiplexed_library, :state => 'passed')
-
-
request.next_requests(@batch.pipeline).each do |sequencing_request|
-
sequencing_request.update_attributes!(:asset => multiplexed_library)
-
end
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Tasks::AssignTagsToTubesHandler
-
-
1
def do_assign_tags_to_destination_task(task, params)
-
@tag_group = TagGroup.find(params[:tag_group])
-
-
ActiveRecord::Base.transaction do
-
@batch.requests.each do |request|
-
tag_id = params[:tag][request.id.to_s] or next
-
tag = @tag_group.tags.find(tag_id)
-
tag.tag!(request.target_asset)
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
module Tasks::AssignTagsToWellsHandler
-
1
def render_assign_tags_to_wells_task(task, params)
-
@tag_group = TagGroup.find(params[:tag_group])
-
@tags = @tag_group.tags
-
@plate = task.find_plates_from_batch(params[:batch_id])
-
requests = task.find_batch_requests(params[:batch_id])
-
@tags_to_wells = task.map_tags_to_wells(@tag_group, @plate)
-
@asset_ids_to_colour_index = task.map_asset_ids_to_normalised_index_by_submission(requests)
-
begin
-
task.validate_tags_not_repeated_for_submission!(requests, @tags_to_wells)
-
rescue
-
flash[:warning] = "Duplicate tags will be assigned to a pooled tube, select a different tag group"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (0).to_s, :tag_group => params[:tag_group]
-
return false
-
end
-
end
-
-
1
def do_assign_tags_to_wells_task(task, params)
-
requests = task.find_batch_requests(params[:batch_id])
-
begin
-
task.validate_returned_tags_are_not_repeated_in_submission!(requests, params)
-
rescue
-
flash[:warning] = "Duplicate tags in a single pooled tube"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (0).to_s, :tag_group => params[:tag_group]
-
return false
-
end
-
-
ActiveRecord::Base.transaction do
-
well_id_tag_id_map = {}
-
params[:tag].each do |k,v|
-
well_id_tag_id_map[k.to_i] = v.to_i
-
end
-
task.assign_tags_to_wells(requests, well_id_tag_id_map)
-
#task.unlink_tag_instances_from_wells(requests)
-
#task.create_tag_instances_and_link_to_wells(requests, params)
-
#task.link_pulldown_indexed_libraries_to_multiplexed_library(requests)
-
end
-
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2014,2015 Genome Research Ltd.
-
1
module Tasks::AssignTubesToWellsHandler
-
1
MAX_SMRT_CELLS_PER_WELL = 7
-
-
1
def render_assign_tubes_to_wells_task(task, params)
-
available_tubes = uniq_assets_from_requests
-
@available_tubes_options = [['',nil]] | available_tubes.map{ |t| [t.name, t.id] }
-
-
@tubes = calculate_number_of_wells_library_needs_to_use(task, params)
-
end
-
-
1
def do_assign_tubes_to_wells_task(task, params)
-
tubes_to_well_positions = tubes_to_wells(params)
-
library_tubes = uniq_assets_from_requests
-
-
requests = task.find_batch_requests(params[:batch_id])
-
-
ActiveRecord::Base.transaction do
-
plate = Plate.create!(:barcode => PlateBarcode.create.barcode)
-
-
library_tubes.each do |library_tube|
-
library_well_positions = all_well_positions_for_library_tube(tubes_to_well_positions, library_tube)
-
requests.select{ |request| request.asset == library_tube }.each_slice(MAX_SMRT_CELLS_PER_WELL) do |requests_for_library|
-
well_position = library_well_positions.shift
-
raise "Not enough well positions to satisfy requests" if well_position.nil?
-
-
well = find_target_asset_from_requests(requests_for_library)
-
well.update_attributes!(:map => Map.find_by_description_and_asset_size(well_position, 96), :plate => plate)
-
assign_requests_to_well(requests_for_library, well)
-
-
end
-
end
-
end
-
-
true
-
end
-
-
# Plate is an option parameter for a target plate. Used in multiplexed
-
# cherrypicking. In its absence a plate is created
-
1
def do_assign_requests_to_multiplexed_wells_task(task, params, plate = nil)
-
-
plate ||= find_or_create_plate(task.purpose)
-
-
well_hash= Hash[plate.wells.located_at(params[:request_locations].values.uniq).map {|w| [w.map_description,w]}]
-
-
problem_wells = wells_with_duplicates(params)
-
-
if problem_wells.present?
-
flash[:error] = "Duplicate tags in #{problem_wells.join(',')}"
-
return false
-
end
-
-
incompatible_wells = find_incompatible_wells(params)
-
-
if incompatible_wells.present?
-
flash[:error] = "Incompatible requests in #{incompatible_wells.join(',')}"
-
return false
-
end
-
-
-
@batch.requests.each do |request|
-
target_well = params[:request_locations][request.id.to_s]
-
request.target_asset = well_hash[target_well]
-
request.save!
-
end
-
true
-
end
-
-
1
def do_assign_pick_volume_task(task, params)
-
@batch.requests.each do |r|
-
next if r.target_asset.nil?
-
r.target_asset.set_picked_volume(params[:micro_litre_volume_required].to_i)
-
end
-
true
-
end
-
-
# Identifies and array of well map descriptions that contain duplicate tags
-
# First filters out any equivalent aliquots. (ie. same sample, tag, library_type, etc.)
-
1
def wells_with_duplicates(params)
-
invalid_wells = []
-
@batch.requests.group_by {|request| params[:request_locations][request.id.to_s]}.each do |well,requests|
-
all_aliquots = requests.map {|r| r.asset.aliquots }.flatten
-
# Push each aliquot onto an array as long as it doesn't match an aliquot already on the array
-
unique_aliquots = all_aliquots.reduce([]) do |selected_aliquots,candidate|
-
selected_aliquots << candidate unless selected_aliquots.any? {|existing_aliquot| existing_aliquot.equivalent?(candidate) }
-
selected_aliquots
-
end
-
# uniq! returns any duplicates, or nil if there are none
-
next if unique_aliquots.map(&:tag_id).uniq!.nil?
-
invalid_wells << well
-
end
-
invalid_wells
-
end
-
1
private :wells_with_duplicates
-
-
1
def find_incompatible_wells(params)
-
invalid_wells = []
-
@batch.requests.group_by {|request| params[:request_locations][request.id.to_s]}.each do |well,requests|
-
next if requests.map {|r| r.shared_attributes }.uniq.count <= 1
-
invalid_wells << well
-
end
-
invalid_wells
-
end
-
1
private :find_incompatible_wells
-
-
1
def find_or_create_plate(purpose)
-
first_request = @batch.requests.first
-
first_request.target_asset.try(:plate)||purpose.create!
-
end
-
-
1
def find_target_asset_from_requests(requests)
-
requests.map{ |request| request.target_asset }.select{ |asset| ! asset.nil? }.first
-
end
-
-
1
def assign_requests_to_well(requests,well)
-
requests.each do |request|
-
request.update_attributes!(:target_asset => well)
-
end
-
end
-
-
1
def all_well_positions_for_library_tube(tubes_to_well_positions, library_tube)
-
tubes_to_well_positions.select{ |tube_to_well| tube_to_well[0] == library_tube }.map{ |tube_to_well| tube_to_well[1] }
-
end
-
-
1
def tubes_to_wells(params)
-
tubes_to_well_positions = []
-
params[:well].each do |well_position, asset_id|
-
next if asset_id.blank?
-
tubes_to_well_positions << [PacBioLibraryTube.find(asset_id.to_i), well_position]
-
end
-
-
tubes_to_well_positions
-
end
-
-
1
def assets_from_requests
-
@afr ||= @batch.requests.map{ |request| request.asset }
-
end
-
-
1
def uniq_assets_from_requests
-
@uafr||=assets_from_requests.uniq
-
end
-
-
1
def assets_from_requests_sorted_by_id
-
@asbi||=assets_from_requests.sort{ |a,b| a.id <=> b.id }
-
end
-
-
1
def calculate_number_of_wells_library_needs_to_use(task, params)
-
tubes_for_wells = []
-
assets = assets_from_requests_sorted_by_id
-
physical_library_tubes = uniq_assets_from_requests
-
-
physical_library_tubes.each do |library_tube|
-
number_of_wells = ((assets.select{ |asset| asset == library_tube }.size.to_f) / MAX_SMRT_CELLS_PER_WELL).ceil
-
next if number_of_wells == 0
-
1.upto(number_of_wells) do
-
tubes_for_wells << library_tube
-
end
-
end
-
-
tubes_for_wells
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Tasks::AttachInfiniumBarcodeHandler
-
1
def render_attach_infinium_barcode_task(task, params)
-
@studies = @batch.studies
-
end
-
-
1
def do_attach_infinium_barcode_task(task, params)
-
barcodes = params[:barcodes]
-
barcodes.each do |plate_id,barcode|
-
next if barcode.blank?
-
plate = Plate.find_by_id(plate_id)
-
return false if plate.nil?
-
# TODO[xxx]: validation of the infinium barcode should be in Plate::Metadata class
-
return false unless plate.valid_infinium_barcode?(barcode)
-
plate.infinium_barcode = barcode
-
end
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Tasks::BindingKitBarcodeHandler
-
1
def render_binding_kit_barcode_task(task, params)
-
end
-
-
1
def do_binding_kit_barcode_task(task, params)
-
barcode = params[:binding_kit_barcode]
-
if barcode.blank?
-
flash[:error] = "Please enter a Kit Barcode"
-
return false
-
end
-
-
requests = task.find_batch_requests(params[:batch_id])
-
ActiveRecord::Base.transaction do
-
requests.each do |request|
-
request.asset.pac_bio_library_tube_metadata.update_attributes!(:binding_kit_barcode => barcode)
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
module Tasks::CherrypickGroupBySubmissionHandler
-
1
def do_cherrypick_group_by_submission_task(task,params)
-
if ! task.valid_params?(params)
-
flash[:warning] = "Invalid values typed in"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (0).to_s
-
return false
-
end
-
-
# If there was a plate scanned then we take its purpose, otherwise we use the purpose specified
-
# in the dropdown.
-
partial_plate, plate_purpose = nil, PlatePurpose.find(params[:plate_purpose_id])
-
if not params[:existing_plate].blank?
-
partial_plate, plate_purpose = Plate.with_machine_barcode(params[:existing_plate]).first, nil
-
if partial_plate.nil?
-
flash[:error] = "Cannot find the partial plate #{params[:existing_plate].inspect}"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (0).to_s
-
return false
-
end
-
unless task.plate_purpose_options(@batch).include?(@plate.purpose)
-
flash[:error] = "Invalid target plate, wrong plate purpose"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return
-
end
-
end
-
-
robot = Robot.find(params[:robot_id])
-
batch = Batch.find(params[:batch_id], :include => [:requests, :pipeline, :lab_events])
-
-
ActiveRecord::Base.transaction do
-
task.send(
-
:"pick_by_#{params[:cherrypick][:action]}",
-
batch.ordered_requests, robot, partial_plate || plate_purpose, params
-
)
-
end
-
-
true
-
rescue => exception
-
flash[:error] = exception.message
-
false
-
end
-
-
1
def render_cherrypick_group_by_submission_task(task,params)
-
@plate_purpose_options = task.plate_purpose_options(@batch)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
module Tasks::CherrypickHandler
-
1
def self.included(base)
-
1
base.class_eval do
-
1
include Cherrypick::Task::PickHelpers
-
end
-
end
-
-
1
def render_cherrypick_task(task, params)
-
unless flash[:error].blank?
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return
-
end
-
-
plate_template = nil
-
unless params[:plate_template].blank?
-
plate_template = PlateTemplate.find(params[:plate_template]["0"].to_i)
-
end
-
if plate_template.nil?
-
flash[:error] = "Please select a template"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return
-
end
-
-
setup_input_params_for_pass_through
-
-
@batch = Batch.find(params[:batch_id], :include => [:requests, :pipeline, :lab_events])
-
@requests = @batch.ordered_requests
-
-
@plate = nil
-
@plate_barcode = params[:existing_plate]
-
@fluidigm_plate = params[:fluidigm_plate]
-
-
if @plate_barcode.present?
-
plate_barcode_id = @plate_barcode.to_i > 11 ? Barcode.number_to_human(@plate_barcode) : @plate_barcode
-
@plate = Plate.find_by_barcode(plate_barcode_id)
-
if @plate.nil?
-
flash[:error] = "Invalid plate barcode"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return
-
end
-
elsif @fluidigm_plate.present?
-
if @fluidigm_plate.size > 10
-
flash[:error] = "Invalid fluidigm barcode"
-
redirect_to :action => 'stage', :batch_id => @batch.id, :workflow_id => @workflow.id, :id => (@stage -1).to_s
-
return
-
end
-
@plate = Plate::Metadata.find(:first, :include=>:plate, :conditions=>{:fluidigm_barcode=> @fluidigm_barcode }).try(:plate)
-
end
-
-
@plate_purpose = PlatePurpose.find(params[:plate_purpose_id])
-
flash.now[:warning] = I18n.t("cherrypick.picking_by_row") if @plate_purpose.cherrypick_in_rows?
-
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
if @spreadsheet_layout
-
@map_info = @spreadsheet_layout
-
elsif @plate.present?
-
@map_info = @task.pick_onto_partial_plate(@requests,plate_template,@robot,@batch,@plate)
-
else
-
@map_info = @task.pick_new_plate(@requests, plate_template, @robot, @batch, @plate_purpose)
-
end
-
@plates = @map_info[0]
-
@source_plate_ids = @map_info[1]
-
-
@plate_cols = @plate.try(:width)||@plate_purpose.plate_width
-
@plate_rows = @plate.try(:height)||@plate_purpose.plate_height
-
end
-
-
1
def setup_input_params_for_pass_through
-
@robot_id = params[:robot_id]
-
@robot = Robot.find(@robot_id)
-
@plate_type = params[:plate_type]
-
@volume_required= params[:volume_required]
-
@micro_litre_volume_required= params[:micro_litre_volume_required]
-
@concentration_required = params[:concentration_required]
-
@minimum_volume = params[:minimum_volume]
-
@maximum_volume = params[:maximum_volume]
-
@total_nano_grams = params[:total_nano_grams]
-
@cherrypick_action = params[:cherrypick][:action]
-
@plate_purpose_id = params[:plate_purpose_id]
-
@fluidigm_barcode = params[:fluidigm_plate]
-
end
-
-
1
def do_cherrypick_task(task, params)
-
plates = params[:plate]
-
size = params[:plate_size]
-
plate_type = params[:plate_type]
-
-
ActiveRecord::Base.transaction do
-
# Determine if there is a standard plate to use.
-
partial_plate, plate_barcode, fluidigm_plate = nil, params[:plate_barcode], params[:fluidigm_plate]
-
unless plate_barcode.nil?
-
partial_plate = Plate.find_by_barcode(plate_barcode) or raise ActiveRecord::RecordNotFound, "No plate with barcode #{plate_barcode.inspect}"
-
end
-
if fluidigm_plate.present?
-
partial_plate = Plate::Metadata.find_by_fluidigm_barcode(fluidigm_plate).try(:plate)
-
end
-
-
# Ensure that we have a plate purpose for any plates we are creating
-
plate_purpose = PlatePurpose.find(params[:plate_purpose_id])
-
asset_shape_id = plate_purpose.asset_shape_id
-
-
# Configure the cherrypicking action based on the parameters
-
cherrypicker = case params[:cherrypick_action]
-
when 'nano_grams_per_micro_litre' then create_nano_grams_per_micro_litre_picker(params)
-
when 'nano_grams' then create_nano_grams_picker(params)
-
when 'micro_litre' then create_micro_litre_picker(params)
-
else raise StandardError, "Invalid cherrypicking type #{params[:cherrypick_action]}"
-
end
-
-
# We can preload the well locations so that we can do efficient lookup later.
-
well_locations = Hash[Map.where_plate_size(partial_plate.try(:size) || size).where_plate_shape(partial_plate.try(:asset_shape) || asset_shape_id).in_row_major_order.map do |location|
-
[location.description, location]
-
end]
-
-
# All of the requests we're going to be using should be part of the batch. If they are not
-
# then we have an error, so we can pre-map them for quick lookup. We're going to pre-cache a
-
# whole load of wells so that they can be retrieved quickly and easily.
-
wells = Hash[Well.find(@batch.requests.map(&:target_asset_id), :include => :well_attribute).map { |w| [w.id,w] }]
-
request_and_well = Hash[@batch.requests.all(:include => :request_metadata).map { |r| [r.id.to_i, [r, wells[r.target_asset_id]]] }]
-
used_requests, plates_and_wells, plate_and_requests = [], Hash.new { |h,k| h[k] = [] }, Hash.new { |h,k| h[k] = [] }
-
-
# If we overflow the plate we create a new one, even if we subsequently clear the fields.
-
plates_with_samples = plates.reject {|pid,rows| rows.values.map(&:values).flatten.all?(&:empty?) }
-
-
if fluidigm_plate.present? && plates_with_samples.count > 1
-
raise Cherrypick::Error, 'Sorry, You cannot pick to multiple fluidigm plates in one batch.'
-
end
-
-
plates_with_samples.each do |id, plate_params|
-
# The first time round this loop we'll either have a plate, from the partial_plate, or we'll
-
# be needing to create a new one.
-
plate = partial_plate
-
if plate.nil?
-
barcode = PlateBarcode.create.barcode
-
plate = plate_purpose.create!(:do_not_create_wells, :name => "Cherrypicked #{barcode}", :size => size, :barcode => barcode, :plate_metadata_attributes=>{:fluidigm_barcode=>fluidigm_plate})
-
end
-
-
# Set the plate type, regardless of what it was. This may change the standard plate.
-
plate.set_plate_type(plate_type) unless plate_type.nil?
-
-
plate_params.each do |row, row_params|
-
row = row.to_i
-
row_params.each do |col, request_id|
-
request, well = case
-
when request_id.blank? then next
-
when request_id.match(/control/) then create_control_request_and_add_to_batch(task, request_id)
-
else request_and_well[request_id.gsub('well_','').to_i] or raise ActiveRecord::RecordNotFound, "Cannot find request #{request_id.inspect}"
-
end
-
-
# NOTE: Performance enhancement here
-
# This collects the wells together for the plate they should be on, and modifies
-
# the values in the well data. It *does not* save either of these, which means that
-
# SELECT & INSERT/UPDATE are not interleaved, which affects the cache
-
well.map = well_locations[plate.asset_shape.location_from_row_and_column(row, col.to_i+1, plate.size)]
-
cherrypicker.call(well, request)
-
plates_and_wells[plate] << well
-
plate_and_requests[plate] << request
-
used_requests << request
-
end
-
-
end
-
-
# At this point we can consider ourselves finished with the partial plate
-
partial_plate = nil
-
end
-
-
# Attach the wells into their plate for maximum efficiency.
-
plates_and_wells.each do |plate, wells|
-
wells.map { |w| w.well_attribute.save! ; w.save! }
-
plate.wells.attach(wells)
-
end
-
-
plate_and_requests.each do |target_plate,requests|
-
Plate.with_requests(requests).each do |source_plate|
-
AssetLink::Job.create(source_plate,[target_plate])
-
end
-
end
-
-
# Now pass each of the requests we used and ditch any there weren't back into the inbox.
-
used_requests.map(&:pass!)
-
(@batch.requests-used_requests).each do |unused_request|
-
unused_request.recycle_from_batch!
-
end
-
end
-
end
-
-
1
def create_control_request_and_add_to_batch(task,control_param)
-
control_request = task.create_control_request_from_well(control_param) or raise StandardError, "Control request not created!"
-
@batch.requests << control_request
-
[control_request, control_request.target_asset]
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014 Genome Research Ltd.
-
1
module Tasks::DnaQcHandler
-
1
def render_dna_qc_task(task, params)
-
@batch = Batch.find(params[:batch_id], :include => [{ :requests => :request_metadata }, :pipeline, :lab_events])
-
@batch.start!(current_user) if @batch.pending?
-
@rits = @batch.pipeline.request_information_types
-
@requests = @batch.requests.includes(
-
:source_well => [
-
:external_properties,
-
:map,
-
:plate,
-
:well_attribute,
-
{ :aliquots => [ :tag, { :sample => :sample_metadata } ] }
-
]).order('maps.column_order ASC').all
-
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
@task = task # @workflow.tasks[params[:id].to_i]
-
@stage = params[:id].to_i
-
@count = 0
-
-
@qc_results = @requests.map { |request| @task.create_render_element(request) }
-
end
-
-
1
def do_dna_qc_task(task, params)
-
ActiveRecord::Base.transaction do
-
params.each do |request_id, value|
-
next unless request_id.to_i != 0
-
request = Request.find request_id
-
next unless request
-
-
task.pass_request(request, @batch, value[:qc_state])
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Tasks::GenerateManifestHandler
-
1
def manifest_filename(name,number)
-
[name, number.to_s, "manifest.csv"].join("_").gsub(/\s/,"_").gsub(/[^A-Za-z0-9_\-\.]/,"")
-
end
-
-
1
def generate_manifest
-
batch = Batch.find(params[:id])
-
study = Study.find(params[:study_id])
-
csv_string = GenerateManifestsTask.generate_manifests(batch,study)
-
send_data csv_string,
-
:type => "text/csv",
-
:filename=> manifest_filename(study.name,batch.id),
-
:disposition => 'attachment'
-
end
-
-
1
def render_generate_manifest_task(task, params)
-
@studies = @batch.studies
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2014 Genome Research Ltd.
-
1
module Tasks::MovieLengthHandler
-
1
def render_movie_length_task(task, params)
-
@valid_movie_lengths = task.descriptors.find_by_name('Movie length').selection
-
@default_movie_length = task.descriptors.find_by_name('Movie length').value.to_i
-
@assets = task.find_batch_requests(params[:batch_id]).map{ |request| request.asset }.uniq
-
end
-
-
1
def do_movie_length_task(task, params)
-
ActiveRecord::Base.transaction do
-
params[:asset].each do |asset_id, movie_length|
-
asset = Asset.find(asset_id)
-
-
unless task.valid_movie_length?(movie_length)
-
flash[:error] = "Invalid movie length"
-
return false
-
end
-
-
asset.pac_bio_library_tube_metadata.update_attributes!(:movie_length => movie_length)
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013 Genome Research Ltd.
-
1
module Tasks
-
1
module PlatePurposeBehavior
-
# Returns a list of valid plate purpose types based on the requests in the current batch.
-
1
def plate_purpose_options(batch)
-
requests = batch.requests.map { |r| r.submission ? r.submission.next_requests(r) : [] }.flatten
-
plate_purposes = requests.map(&:request_type).compact.uniq.map(&:acceptable_plate_purposes).flatten.uniq
-
plate_purposes = PlatePurpose.cherrypickable_as_target.all if plate_purposes.empty? # Fallback situation for the moment
-
plate_purposes.map { |p| [p.name, p.size, p.id] }.sort
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2015 Genome Research Ltd.
-
1
module Tasks::PlateTemplateHandler
-
1
def render_plate_template_task(task, params)
-
@robots = Robot.all
-
set_plate_purpose_options(task)
-
suitable_sizes = @plate_purpose_options.map {|o| o[1] }.uniq
-
if (@batch.pipeline.control_request_type.nil?)
-
@plate_templates = PlateTemplate.with_sizes(suitable_sizes).select(&:without_control_wells?)
-
else
-
@plate_templates = PlateTemplate.with_sizes(suitable_sizes)
-
end
-
end
-
-
1
def set_plate_purpose_options(task)
-
@plate_purpose_options = task.plate_purpose_options(@batch)
-
end
-
-
1
def do_plate_template_task(task, params)
-
return true if params[:file].blank?
-
-
if params[:plate_template].blank?
-
plate_size = PlatePurpose.find(params[:plate_purpose_id]).size
-
else
-
plate_size = PlateTemplate.find(params[:plate_template]["0"].to_i).size
-
end
-
-
parsed_plate_details = parse_uploaded_spreadsheet_layout(params[:file].read,plate_size)
-
@spreadsheet_layout = map_parsed_spreadsheet_to_plate(parsed_plate_details,@batch,plate_size)
-
-
true
-
end
-
-
1
def parse_uploaded_spreadsheet_layout(layout_data,plate_size)
-
(Hash.new { |h,k| h[k] = {} }).tap do |parsed_plates|
-
CSV.parse(layout_data, :headers=>:first_row) do |row|
-
parse_spreadsheet_row(plate_size, row["Request ID"],row["Sample Name"],row["Plate"],row["Destination Well"]) do |plate_key, request_id, location|
-
parsed_plates[plate_key][location.column_order] = [location,request_id]
-
end
-
end
-
end
-
end
-
1
private :parse_uploaded_spreadsheet_layout
-
-
1
def parse_spreadsheet_row(plate_size, request_id, asset_name, plate_key, destination_well)
-
return if request_id.blank? or request_id.to_i == 0
-
return if destination_well.blank? or destination_well.to_i > 0
-
-
location = Map.find_by_description_and_asset_size(destination_well, plate_size) or return
-
plate_key = "default plate 1" if plate_key.blank?
-
yield(plate_key, request_id.to_i, location)
-
end
-
1
private :parse_spreadsheet_row
-
-
1
def map_parsed_spreadsheet_to_plate(mapped_plate_wells,batch,plate_size)
-
plates = mapped_plate_wells.map do |plate_key, mapped_wells|
-
(0...plate_size).map do |i|
-
well, location, request_id = CherrypickTask::EMPTY_WELL, *mapped_wells[i]
-
if request_id.present?
-
asset = batch.requests.find(request_id).asset
-
well = [request_id, asset.plate.barcode, asset.display_name]
-
end
-
well
-
end
-
end
-
-
[ plates, plates.map { |_, barcode, _| barcode }.uniq ]
-
end
-
1
private :map_parsed_spreadsheet_to_plate
-
-
1
def self.generate_spreadsheet(batch)
-
CSV.generate(:row_sep => "\r\n") do |csv|
-
csv << ["Request ID","Sample Name","Plate","Destination Well"]
-
batch.requests.each{ |r| csv << [r.id,r.asset.sample.name,"",""]}
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014,2015 Genome Research Ltd.
-
1
module Tasks::PlateTransferHandler
-
-
1
class InvalidBatch < StandardError; end
-
-
1
def render_plate_transfer_task(task,params)
-
ActiveRecord::Base.transaction do
-
@target = find_or_create_target(task)
-
end
-
end
-
-
1
def includes_for_plate_creation
-
[{:asset=>[:map,{:plate=>[:plate_purpose,:barcode_prefix]},:aliquots]},{:target_asset=>[:pac_bio_library_tube_metadata]}]
-
end
-
-
1
def find_or_create_target(task)
-
return target_plate if target_plate.present?
-
# We only eager load the request stuff if we actually need it.
-
batch_requests = @batch.requests.find(:all,:include=>includes_for_plate_creation)
-
source_wells = batch_requests.map {|r| r.asset}
-
raise InvalidBatch if unsuitable_wells?(source_wells)
-
-
transfer_request_to_plate = RequestType.find_by_target_purpose_id(task.purpose_id)||RequestType.transfer
-
transfer_request_from_plate = RequestType.transfer
-
task.purpose.create!.tap do |target|
-
-
well_map = Hash[target.wells.map {|well| [well.map_id,well] }]
-
-
batch_requests.each do |outer_request|
-
source = outer_request.asset
-
transfer_request_to_plate.create!(
-
:asset => source,
-
:target_asset => well_map[source.map_id],
-
:submission_id => outer_request.submission_id
-
)
-
transfer_request_from_plate.create!(
-
:asset => well_map[source.map_id],
-
:target_asset => outer_request.target_asset,
-
:submission_id => outer_request.submission_id
-
)
-
end
-
end
-
end
-
-
1
def target_plate
-
transfer = TransferRequest.siblings_of(@batch.requests.first).for_submission_id(@batch.requests.first.submission_id).find(:first,:include=>{:target_asset=>:plate})
-
return nil unless transfer.present?
-
transfer.target_asset.plate
-
end
-
-
1
def unsuitable_wells?(source_wells)
-
(source_wells - source_wells.first.plate.wells.with_contents).present?
-
end
-
1
private :unsuitable_wells?
-
-
1
def do_plate_transfer_task(task,params)
-
target_plate.transition_to('passed',current_user) unless target_plate.state=='passed'
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Tasks::PrepKitBarcodeHandler
-
1
def render_prep_kit_barcode_task(task, params)
-
end
-
-
1
def do_prep_kit_barcode_task(task, params)
-
barcode = params[:prep_kit_barcode].strip
-
if barcode.blank?
-
flash[:error] = "Please enter a Kit Barcode"
-
return false
-
end
-
-
requests = @batch.requests
-
ActiveRecord::Base.transaction do
-
requests.each do |request|
-
request.target_asset.pac_bio_library_tube_metadata.update_attributes!(:prep_kit_barcode => barcode)
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Tasks::ReferenceSequenceHandler
-
1
def render_reference_sequence_task(task, params)
-
@assets = task.find_batch_requests(params[:batch_id]).map{ |request| request.asset }.uniq
-
end
-
-
1
def do_reference_sequence_task(task, params)
-
ActiveRecord::Base.transaction do
-
params[:asset].each do |asset_id, protocol_id|
-
protocol = ReferenceGenome.find(protocol_id).name
-
if protocol.blank?
-
flash[:warning] = 'All samples must have a protocol selected'
-
return false
-
end
-
-
Asset.find(asset_id).pac_bio_library_tube_metadata.update_attributes!(:protocol => protocol)
-
end
-
end
-
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2014 Genome Research Ltd.
-
1
module Tasks::SamplePrepQcHandler
-
1
def render_sample_prep_qc_task(task, params)
-
@requests = task.find_batch_requests(params[:batch_id])
-
end
-
-
1
def do_sample_prep_qc_task(task, params)
-
requests = task.find_batch_requests(params[:batch_id])
-
-
params[:request].each do |request_id, qc_status|
-
requests_found = requests.select{ |request| request.id == request_id.to_i }
-
request = requests_found.first
-
if request.nil?
-
flash[:error] = "Couldnt find Request #{request_id}"
-
return false
-
end
-
if qc_status == "failed"
-
request.fail!
-
elsif qc_status == "passed"
-
request.pass!
-
request.target_asset.pac_bio_library_tube_metadata.update_attributes!(:smrt_cells_available => 1)
-
else
-
flash[:error] = "Invalid QC state for #{request_id}"
-
return false
-
end
-
end
-
-
true
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2013 Genome Research Ltd.
-
1
module Tasks::SetCharacterisationDescriptorsHandler
-
1
def do_set_characterisation_descriptors_task(task, params)
-
-
@count = 0
-
if params[:values].nil?
-
@values = {}
-
else
-
@values = params[:values]
-
end
-
-
# Perform the necessary updates if we've passed batch creation
-
updated = 0
-
-
@batch.requests.each do |request|
-
-
event = LabEvent.new(:batch_id => @batch.id, :description => @task.name)
-
-
if params[:requests].present? && params[:requests]["#{request.id}"].present? && params[:requests]["#{request.id}"][:descriptors].present?
-
# Descriptors: create description for event
-
-
event.descriptors = params[:requests]["#{request.id}"][:descriptors]
-
event.descriptor_fields = ordered_fields(params[:requests]["#{request.id}"][:fields])
-
-
end
-
-
event.save!
-
current_user.lab_events << event
-
request.lab_events << event
-
-
-
unless request.asset.try(:resource)
-
EventSender.send_request_update(request.id, "update", "Passed: #{@task.name}")
-
end
-
-
if request.has_passed(@batch, @task) || request.failed?
-
updated += 1
-
end
-
end
-
-
-
# Did all the requests get updated?
-
if updated == @batch.requests.count
-
eventify_batch @batch, @task
-
return true
-
else
-
# Some requests have yet to pass this task
-
# Construct a URL that contains a nested hash of values to display as defaults for the next request
-
@params = { :batch_id => @batch.id, :workflow_id => @workflow.id, :values => @values }
-
redirect_to url_for(flatten_hash(@params))
-
end
-
-
false
-
end
-
-
1
def render_set_characterisation_descriptors_task(task, params)
-
@batch = Batch.find(params[:batch_id], :include => [:requests, :pipeline, :lab_events])
-
@rits = @batch.pipeline.request_information_types
-
@requests = @batch.ordered_requests
-
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
@task = @workflow.tasks[params[:id].to_i]
-
@stage = params[:id].to_i
-
@count = 0
-
if params[:values].nil?
-
@values = {}
-
else
-
@values = params[:values]
-
end
-
-
-
end
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013 Genome Research Ltd.
-
1
module Tasks::SetDescriptorsHandler
-
-
1
def do_set_descriptors_task(task, params)
-
@batch = Batch.find(params[:batch_id], :include => [:requests, :pipeline, :lab_events])
-
@rits = @batch.pipeline.request_information_types
-
@requests = @batch.ordered_requests
-
-
# if qc_state is qc_manual then update it
-
if @batch.qc_state == "qc_manual"
-
@batch.lab_events.create(:description => "Manual QC", :message => "Manual QC started for batch #{@batch.id}", :user_id => current_user.id)
-
@batch.lab_events.create(:description => "Manual QC", :message => "Manual QC started for batch #{@batch.id}", :user_id => current_user.id)
-
@batch.qc_state = @batch.qc_next_state
-
@batch.save
-
end
-
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
@task = @workflow.tasks[params[:id].to_i]
-
@stage = params[:id].to_i
-
@count = 0
-
if params[:values].nil?
-
@values = {}
-
else
-
@values = params[:values]
-
end
-
-
# Perform the necessary updates if we've passed batch creation
-
unless params[:next_stage].nil?
-
updated = 0
-
-
@batch.requests.each do |request|
-
unless params[:request].nil?
-
params[:request].keys.each do |checked|
-
# This is used to see if any check boxes are set on when the page is rendered with
-
# with a single set of descriptor fields is shared between all the requests...
-
if request.id == checked.to_i
-
-
event = LabEvent.new(:batch_id => @batch.id, :description => @task.name)
-
-
# This is called when a single set of fields is used
-
# and called over and over based on the select boxs
-
unless params[:descriptors].nil?
-
event.descriptors = params[:descriptors]
-
event.descriptor_fields = ordered_fields(params[:fields])
-
-
# Cache values to populate the next request on the same stage
-
event.descriptors.each do |descriptor|
-
@values[descriptor.name] = descriptor.value
-
end
-
end
-
-
if !params[:requests].nil? && !params[:requests]["#{request.id}"].nil? && !params[:requests]["#{request.id}"][:descriptors].nil?
-
# Descriptors: create description for event
-
-
event.descriptors = params[:requests]["#{request.id}"][:descriptors]
-
event.descriptor_fields = ordered_fields(params[:requests]["#{request.id}"][:fields])
-
-
end
-
-
unless params[:upload].nil?
-
params[:upload].each_key do |key|
-
event.filename = params[:upload][key].original_filename.gsub(/[^a-zA-Z0-9.]/, '_')
-
event.data = params[:upload][key].read
-
event.add_descriptor Descriptor.new({:name => key, :value => event.filename})
-
end
-
end
-
-
event.save
-
current_user.lab_events << event
-
request.lab_events << event
-
-
if params[:asset]
-
params[:asset].keys.each do |key|
-
asset = Asset.new()
-
asset.sti_type = Family.find(params[:asset][key][:family_id]).name
-
params[:asset][key].each_key do |field|
-
asset.add_descriptor Descriptor.new({ :name => field, :value => params[:asset][key][field] })
-
end
-
asset.save
-
asset.parents << request.asset
-
end
-
end
-
-
unless request.asset.try(:resource)
-
EventSender.send_request_update(request.id, "update", "Passed: #{@task.name}")
-
end
-
end
-
end
-
end
-
-
if request.has_passed(@batch, @task) || request.failed?
-
updated += 1
-
end
-
end
-
-
-
if updated == @batch.requests.count
-
eventify_batch @batch, @task
-
return true
-
else
-
# Some requests have yet to pass this task
-
# Construct a URL that contains a nested hash of values to display as defaults for the next request
-
@params = { :batch_id => @batch.id, :workflow_id => @workflow.id, :values => @values }
-
redirect_to url_for(flatten_hash(@params))
-
end
-
-
end
-
false
-
end
-
-
1
def render_set_descriptors_task(task, params)
-
@batch = Batch.find(params[:batch_id], :include => [:requests, :pipeline, :lab_events])
-
@rits = @batch.pipeline.request_information_types
-
@requests = @batch.ordered_requests
-
@workflow = LabInterface::Workflow.find(params[:workflow_id], :include => [:tasks])
-
@task = @workflow.tasks[params[:id].to_i]
-
@stage = params[:id].to_i
-
@count = 0
-
if params[:values].nil?
-
@values = {}
-
else
-
@values = params[:values]
-
end
-
-
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Tasks::SetLocationHandler
-
1
def do_set_location_task(task, params)
-
batch = Batch.find params[:batch_id]
-
location_id = params[:location_id][0].to_i
-
if batch.pipeline.group_by_parent?
-
groups= task.acts_on_input ? batch.input_group : batch.output_group
-
groups.each do |group, requests|
-
next unless group.size>0 and (asset_id = group.first) #wells which hasn't been cherry picked for example
-
task.set_location(asset_id, location_id)
-
end
-
else
-
raise RuntimeError, "Not implemented yet"
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
module Tasks::StartBatchHandler
-
-
1
def do_start_batch_task(task, params)
-
return unless task.lab_activity?
-
Batch.find(params[:batch_id]).start!(current_user) unless @batch.started? or @batch.failed?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
1
module Tasks::StripTubeCreationHandler
-
-
1
def render_strip_tube_creation_task(task,params)
-
-
@tubes_requested = @batch.requests.first.asset.requests.for_pipeline(task.workflow.pipeline).count
-
@tubes_available = @batch.requests.first.asset.requests.for_pipeline(task.workflow.pipeline).pending.count
-
-
strip_count = task.descriptors.find_by_key!('strips_to_create')
-
-
@options = strip_count.selection.select {|v| v <= (@tubes_available)}
-
@default = strip_count.value || @options.last
-
end
-
-
1
def do_strip_tube_creation_task(task,params)
-
tubes_to_create = params['tubes_to_create'].to_i
-
-
locations_requests = @batch.requests.with_asset_location.pending.group_by {|r| r.asset.map.column_order }
-
-
if locations_requests.any? {|k,v| v.count < tubes_to_create }
-
flash[:error] = "There are insufficient requests remaining for the requested number of tubes."
-
flash[:error].concat(" Some wells of the plate have different numbers of requests.") if locations_requests.values.map(&:count).uniq.count > 1
-
return false
-
end
-
-
if locations_requests.keys.sort != [0,1,2,3,4,5,6,7]
-
flash[:error] = "This pipeline only supports wells in the first column."
-
return false
-
end
-
-
input_plate = @batch.requests.first.asset.plate
-
source_plate = input_plate.source_plate||input_plate
-
-
if params['source_plate_barcode'] != input_plate.ean13_barcode
-
flash[:error] = "'#{params['source_plate_barcode']}' is not the correct plate for this batch."
-
return false
-
end
-
-
base_name = source_plate.sanger_human_barcode
-
-
strip_purpose = Purpose.find_by_name(task.descriptors.find_by_key!('strip_tube_purpose').value)
-
-
(0...tubes_to_create).each do |tube_number|
-
-
tube = strip_purpose.create!(:name=>"#{base_name}:#{tube_number+1}",:location=>@batch.pipeline.location)
-
AssetLink::Job.create(source_plate,[tube])
-
-
tube.size.times do |index|
-
request = locations_requests[index].pop
-
well = tube.wells.in_column_major_order.all[index].id
-
request.submission.next_requests(request).each do |dsr|
-
dsr.update_attributes!(:asset_id => well)
-
end
-
request.update_attributes!(:target_asset_id => well)
-
end
-
end
-
-
locations_requests.values.flatten.each do |request|
-
@batch.remove_link(request)
-
request.return_pending_to_inbox!
-
end
-
-
true
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
module Tasks::TagGroupHandler
-
1
def render_tag_groups_task(task, params)
-
@tag_groups = TagGroup.visible.all
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module Tasks::ValidateSampleSheetHandler
-
1
def render_validate_sample_sheet_task(task, params)
-
-
end
-
-
1
def do_validate_sample_sheet_task(task, params)
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class Tecan < RobotVerification
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2015 Genome Research Ltd.
-
1
class Transfer < ActiveRecord::Base
-
1
module Associations
-
1
def self.included(base)
-
4
base.class_eval do
-
4
include Transfer::State
-
-
4
has_many :transfers_as_source, :class_name => 'Transfer', :foreign_key => :source_id, :order => 'created_at ASC'
-
4
has_many :transfers_to_tubes, :class_name => 'Transfer::BetweenPlateAndTubes', :foreign_key => :source_id, :order => 'created_at ASC'
-
4
has_many :transfers_as_destination, :class_name => 'Transfer', :foreign_key => :destination_id, :order => 'id ASC'
-
-
# This looks odd but it's a LEFT OUTER JOIN, meaning that the rows we would be interested in have no source_id.
-
4
scope :with_no_outgoing_transfers, -> {
-
select("DISTINCT #{base.quoted_table_name}.*").
-
joins("LEFT OUTER JOIN `transfers` outgoing_transfers ON outgoing_transfers.`source_id`=#{base.quoted_table_name}.`id`").
-
where('outgoing_transfers.source_id IS NULL')
-
}
-
end
-
end
-
end
-
-
1
module State
-
# These are all of the valid states but keep them in a priority order: in other words, 'started' is more important
-
# than 'pending' when there are multiple requests (like a plate where half the wells have been started, the others
-
# are failed).
-
1
ALL_STATES = [ 'started', 'qc_complete', 'pending', 'nx_in_progress', 'started_fx', 'started_mj', 'passed', 'failed', 'cancelled' ]
-
-
1
def self.state_helper(names)
-
1
Array(names).each do |name|
-
9
module_eval(%Q{def #{name}? ; state == #{name.to_s.inspect} ; end})
-
end
-
end
-
-
1
state_helper(ALL_STATES)
-
-
# The state of an asset is based on the transfer requests for the asset. If they are all in the same
-
# state then it takes that state. Otherwise we take the "most optimum"!
-
1
def state
-
state_from(self.transfer_requests)
-
end
-
-
1
def state_from(state_requests)
-
unique_states = state_requests.map(&:state).uniq
-
return unique_states.first if unique_states.size == 1
-
ALL_STATES.detect { |s| unique_states.include?(s) } || self.default_state || 'unknown'
-
end
-
1
private :state_from
-
-
1
module PlateState
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :in_state, ->(states) {
-
states = Array(states).map(&:to_s)
-
-
# If all of the states are present there is no point in actually adding this set of conditions because we're
-
# basically looking for all of the plates.
-
if states.sort != ALL_STATES.sort
-
# NOTE: The use of STRAIGHT_JOIN here forces the most optimum query on MySQL, where it is better to reduce
-
# assets to the plates, then look for the wells, rather than vice-versa. The former query takes fractions
-
# of a second, the latter over 60.
-
query_conditions, joins = 'transfer_requests_as_target.state IN (?)', [
-
"STRAIGHT_JOIN `container_associations` ON (`assets`.`id` = `container_associations`.`container_id`)",
-
"INNER JOIN `assets` wells_assets ON (`wells_assets`.`id` = `container_associations`.`content_id`) AND (`wells_assets`.`sti_type` = 'Well')",
-
"LEFT OUTER JOIN `requests` transfer_requests_as_target ON transfer_requests_as_target.target_asset_id = wells_assets.id AND (transfer_requests_as_target.`sti_type` IN (#{[TransferRequest, *TransferRequest.descendants].map(&:name).map(&:inspect).join(',')}))"
-
]
-
-
# Note that 'state IS NULL' is included here for plates that are stock plates, because they will not have any
-
# transfer requests coming into their wells and so we can assume they are pending (from the perspective of
-
# pulldown at least).
-
query_conditions = 'transfer_requests_as_target.state IN (?)'
-
if states.include?('pending')
-
joins << "INNER JOIN `plate_purposes` ON (`plate_purposes`.`id` = `assets`.`plate_purpose_id`)"
-
query_conditions << ' OR (transfer_requests_as_target.state IS NULL AND plate_purposes.can_be_considered_a_stock_plate=TRUE)'
-
end
-
-
{ :joins => joins, :conditions => [ query_conditions, states ] }
-
else
-
{ }
-
end
-
}
-
end
-
end
-
end
-
-
1
module TubeState
-
1
def self.included(base)
-
1
base.class_eval do
-
1
scope :in_state, ->(states) {
-
states = Array(states).map(&:to_s)
-
-
# If all of the states are present there is no point in actually adding this set of conditions because we're
-
# basically looking for all of the plates.
-
if states.sort != ALL_STATES.sort
-
-
query_conditions, joins = 'transfer_requests_as_target.state IN (?)', [
-
"LEFT OUTER JOIN `requests` transfer_requests_as_target ON transfer_requests_as_target.target_asset_id = `assets`.id AND (transfer_requests_as_target.`sti_type` IN (#{[TransferRequest, *TransferRequest.descendants].map(&:name).map(&:inspect).join(',')}))"
-
]
-
-
query_conditions = 'transfer_requests_as_target.state IN (?)'
-
-
{ :joins => joins, :conditions => [ query_conditions, states ] }
-
else
-
{ }
-
end
-
}
-
1
scope :without_finished_tubes, ->(purpose) {
-
{:conditions => ["NOT (plate_purpose_id IN (?) AND state = 'passed')", purpose.map(&:id) ]}
-
}
-
end
-
end
-
end
-
end
-
-
# The transfers are described in some manner, like direct transfers of one well to the same well on
-
# another plate.
-
1
module TransfersBySchema
-
1
def self.included(base)
-
2
base.class_eval do
-
2
serialize :transfers
-
2
validates :transfers, :presence => true, :allow_blank => false
-
end
-
end
-
end
-
-
# The transfer goes from the source to a specified destination and this can only happen once.
-
1
module TransfersToKnownDestination
-
1
def self.included(base)
-
5
base.class_eval do
-
5
belongs_to :destination, :polymorphic => true
-
5
validates_presence_of :destination
-
5
validates_uniqueness_of :destination_id, :scope => [ :destination_type, :source_id ], :message => 'can only be transferred to once from the source'
-
end
-
end
-
end
-
-
# The transfer from the source is controlled by some mechanism other than user choice. Essentially
-
# an algorithmic transfer, which is recorded so we know what happened.
-
1
module ControlledDestinations
-
1
def self.included(base)
-
2
base.class_eval do
-
# Ensure that the transfers are recorded so we can see what happened.
-
2
serialize :transfers
-
2
validates_unassigned :transfers
-
end
-
end
-
-
1
def each_transfer(&block)
-
well_to_destination.each do |source, destination_and_additional_information|
-
destination, *extra_information = Array(destination_and_additional_information)
-
yield(source, destination)
-
record_transfer(source, destination, *extra_information)
-
end
-
end
-
1
private :each_transfer
-
end
-
-
1
include Uuid::Uuidable
-
-
1
self.inheritance_column = "sti_type"
-
-
# So we can track who is requesting the transfer
-
1
belongs_to :user
-
1
validates_presence_of :user
-
-
# The source plate and the destination asset (which varies between different types of transfers)
-
# You can only transfer from one plate to another once, anything else is an error.
-
1
belongs_to :source, :class_name => 'Plate'
-
1
validates_presence_of :source
-
1
scope :include_source, -> { includes( :source => ModelExtensions::Plate::PLATE_INCLUDES ) }
-
-
# Before creating an instance of this class the appropriate transfers need to be made from a source
-
# asset to the destination one.
-
1
before_create :create_transfer_requests
-
1
def create_transfer_requests
-
# Note: submission is optional. Unlike methods, blocks don't support default argument
-
# values, but any attributes not yielded will be nil. Apparently 1.9 is more consistent
-
each_transfer do |source, destination, submission|
-
request_type_between(source, destination).create!(
-
:asset => source,
-
:target_asset => destination,
-
:submission_id => submission||source.pool_id
-
)
-
end
-
end
-
1
private :create_transfer_requests
-
-
1
def self.preview!(attributes)
-
new(attributes) do |transfer|
-
raise ActiveRecord::RecordInvalid, transfer unless transfer.valid?
-
transfer.unsaved_uuid!
-
transfer.send(:each_transfer) do |source, destination|
-
# Needs to do nothing at all as the transfers will be recorded
-
end
-
end
-
end
-
-
# Determines if the well should not be transferred.
-
1
def should_well_not_be_transferred?(well)
-
well.nil? or well.aliquots.empty? or well.failed? or well.cancelled?
-
end
-
1
private :should_well_not_be_transferred?
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
1
class Transfer::BetweenPlateAndTubes < Transfer
-
1
DESTINATION_INCLUDES = {
-
:destination => [
-
:uuid_object, {
-
:aliquots => [
-
:uuid_object,
-
:bait_library, {
-
:tag => :tag_group,
-
:sample => [
-
:uuid_object, {
-
:primary_study => { :study_metadata => :reference_genome },
-
:sample_metadata => :reference_genome
-
}
-
]
-
}
-
]
-
}
-
]
-
}
-
-
1
class WellToTube < ActiveRecord::Base
-
1
self.table_name =('well_to_tube_transfers')
-
-
1
belongs_to :transfer, :class_name => 'Transfer::BetweenPlateAndTubes'
-
1
validates_presence_of :transfer
-
-
1
belongs_to :destination, :class_name => 'Tube'
-
1
validates_presence_of :destination
-
-
1
validates_presence_of :source
-
-
1
scope :include_destination, -> { includes(Transfer::BetweenPlateAndTubes::DESTINATION_INCLUDES) }
-
end
-
-
1
include Transfer::ControlledDestinations
-
-
# Records the transfers from the wells on the plate to the assets they have gone into.
-
1
has_many :well_to_tubes, :class_name => 'Transfer::BetweenPlateAndTubes::WellToTube', :foreign_key => :transfer_id
-
1
scope :include_transfers, -> { includes( :well_to_tubes => DESTINATION_INCLUDES ) }
-
-
1
def transfers
-
Hash[well_to_tubes.include_destination.map { |t| [t.source, tube_to_hash(t.destination)] }]
-
end
-
-
# NOTE: Performance enhancement to convert a tube to it's minimal representation for presentation.
-
1
def tube_to_hash(tube)
-
# Only build the hash once per tube. Shows significant speed improvement, esp. with label_text
-
@tubes||={}
-
@tubes[tube.id] ||= {
-
:uuid => tube.uuid,
-
:name => tube.name,
-
:state => tube.state,
-
:label => { :text => tube.purpose.name }
-
}.tap do |details|
-
barcode_to_hash(tube) { |s| details[:barcode] = s }
-
barcode_to_hash(tube.stock_plate) { |s| details[:stock_plate] = { :barcode => s } }
-
details[:label][:prefix] = tube.role unless tube.role.nil?
-
end
-
end
-
1
private :tube_to_hash
-
-
1
def barcode_to_hash(barcoded)
-
yield({
-
:number => barcoded.barcode,
-
:prefix => barcoded.barcode_prefix.prefix,
-
:two_dimensional => barcoded.two_dimensional_barcode,
-
:ean13 => barcoded.ean13_barcode,
-
:type => barcoded.barcode_type
-
}) if barcoded.present?
-
end
-
1
private :barcode_to_hash
-
-
#--
-
# The source plate wells need to be translated back to the stock plate wells, which simply
-
# involves following the transfer requests back up until we hit the stock plate. We only need
-
# to follow one transfer request for each well as the submission related stock wells will be in
-
# the same final well. Once we get to the stock well we then find the request that has the
-
# well as a source and the target is an MX library tube.
-
#++
-
1
def well_to_destination
-
ActiveSupport::OrderedHash[
-
source.stock_wells.map do |well, stock_wells|
-
tube = locate_mx_library_tube_for(well, stock_wells)
-
(tube.nil? or should_well_not_be_transferred?(well)) ? nil : [ well, [ tube, stock_wells ] ]
-
end.compact
-
]
-
end
-
1
private :well_to_destination
-
-
1
def record_transfer(source, destination, stock_well)
-
@transfers ||= {}
-
@transfers[source.map.description] = [ destination, stock_well ]
-
end
-
1
private :record_transfer
-
-
1
after_create :build_well_to_tube_transfers
-
1
def build_well_to_tube_transfers
-
tube_to_stock_wells = Hash.new { |h,k| h[k] = [] }
-
self.well_to_tubes.build(@transfers.map do |source, (destination, stock_wells)|
-
tube_to_stock_wells[destination].concat(stock_wells)
-
{ :source => source, :destination => destination }
-
end).map(&:save!)
-
-
tube_to_stock_wells.each do |tube, stock_wells|
-
tube.update_attributes!(:name => tube_name_for(stock_wells))
-
end
-
end
-
1
private :build_well_to_tube_transfers
-
-
# Builds the name for the tube based on the wells that are being transferred from by finding their stock plate wells and
-
# creating an appropriate range.
-
1
def tube_name_for(stock_wells)
-
source_wells = source.plate_purpose.source_wells_for(stock_wells).sort {|w1,w2| w1.map.column_order <=> w2.map.column_order }
-
stock_plates = source_wells.map(&:plate).uniq
-
raise StandardError, "There appears to be no stock plate!" if stock_plates.empty?
-
raise StandardError, "Cannot handle cross plate pooling!" if stock_plates.size > 1
-
first,last = source_wells.first.map_description, source_wells.last.map_description
-
"#{stock_plates.first.sanger_human_barcode} #{first}:#{last}"
-
end
-
1
private :tube_name_for
-
-
# Request type is based on the destination tube from the source plate
-
1
def request_type_between(_, destination)
-
destination.transfer_request_type_from(source)
-
end
-
1
private :request_type_between
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2015 Genome Research Ltd.
-
# Picks the specified wells from one plate into the wells of another. In this case transfers
-
# is a hash from source to destination well location and destination is the target plate for
-
# the transfers.
-
1
class Transfer::BetweenPlates < Transfer
-
1
extend ::ModelExtensions::Plate::NamedScopeHelpers
-
1
include_plate_named_scope :source
-
1
include_plate_named_scope :destination
-
-
1
include TransfersBySchema
-
1
include TransfersToKnownDestination
-
1
include BuildsStockWellLinks
-
-
1
include Asset::Ownership::ChangesOwner
-
1
set_target_for_owner(:destination)
-
-
# The values in the transfers must be a hash and must be valid well positions on both the
-
# source and destination plates.
-
1
validates_each(:transfers) do |record, attribute, value|
-
if not value.is_a?(Hash)
-
record.errors.add(:transfers, 'must be a map from source to destination location')
-
elsif record.source.present? and not record.source.valid_positions?(value.keys)
-
record.errors.add(:transfers, 'are not valid positions for the source plate')
-
elsif record.destination.present? and not record.destination.valid_positions?(value.values.flatten)
-
record.errors.add(:transfers, "#{value.values.inspect} are not valid positions for the destination plate")
-
end
-
end
-
-
#--
-
# Transfers between plates may encounter empty source wells, in which case we don't bother
-
# making that transfer. In the case of the pulldown pipeline this could happen after the
-
# plate has been put on the robot, as the number of columns transfered could be less than
-
# an entire plate. Subsequent plates are therefore only partially complete.
-
#++
-
1
def each_transfer(&block)
-
# Partition the source plate wells into ones that are good and others that are bad. The
-
# bad wells will be eliminated after we've done the transfers for the good ones.
-
bad_wells, good_wells = source.wells.located_at_position(transfers.keys).with_pool_id.partition(&method(:should_well_not_be_transferred?))
-
source_wells = Hash[good_wells.map { |well| [well.map.description, well] }]
-
destination_locations = source_wells.keys.map { |p| transfers[p] }.flatten
-
destination_wells = Hash[destination.wells.located_at_position(destination_locations).map { |well| [well.map_description, well] }]
-
-
# Build a list of source wells for each destination well.
-
dest_sources = Hash.new {|h,i| h[i] = Array.new }
-
transfers.each {|source,dests| dests.each {|dest| dest_sources[dest] << source }} if destination.supports_multiple_submissions?
-
-
pcg = source.pre_cap_groups
-
location_subs = dest_sources.inject({}) do |store, dest_source|
-
dest_loc, sources = *dest_source
-
uuid, transfer_details = pcg.detect {|k,v| v[:wells].sort == sources.sort}
-
raise StandardError, "Could not find appropriate pool" if transfer_details.nil?
-
pcg.delete(uuid)
-
store[dest_loc] = transfer_details[:submission_id]
-
store
-
end
-
-
source_wells.each do |location, source_well|
-
Array(transfers[location]).flatten.each do |target_well_location|
-
yield(source_well, destination_wells[target_well_location],location_subs[target_well_location])
-
end
-
end
-
-
# Eliminate any of the transfers that were not made because of the bad source wells
-
transfers_we_did_not_make = bad_wells.map { |well| well.map.description }
-
transfers.delete_if { |k,_| transfers_we_did_not_make.include?(k) }
-
end
-
1
private :each_transfer
-
-
# Request type for transfers is based on the plates, not the wells we're transferring
-
1
def request_type_between(ignored_a, ignored_b)
-
destination.transfer_request_type_from(source)
-
end
-
1
private :request_type_between
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2015 Genome Research Ltd.
-
# This is effectively pooling: all wells that have come from the same submission will be transferred
-
# into the same well on the destination plate.
-
1
class Transfer::BetweenPlatesBySubmission < Transfer
-
1
extend ::ModelExtensions::Plate::NamedScopeHelpers
-
1
include_plate_named_scope :source
-
1
include_plate_named_scope :destination
-
-
1
include TransfersToKnownDestination
-
1
include ControlledDestinations
-
1
include BuildsStockWellLinks
-
-
1
include Asset::Ownership::ChangesOwner
-
1
set_target_for_owner(:destination)
-
-
#--
-
# Track back from the specified well to the stock plate well that has been transfered here. Then
-
# find the non-transfer request where the stock well was the source asset, and from there the submission
-
# that it came from. Hence all stock wells that are part of the same submission will be transferred to
-
# the same well.
-
#++
-
1
def well_to_destination
-
{}.tap do |sources_to_target|
-
# Group the wells based on the submission
-
groups = source.wells.in_column_major_order.with_pool_id.group_by(&:pool_id).delete_if { |k,_| k.nil? }.values
-
-
# Submission group 1 will go into A1, group 2 into B1, group 3 C1, etc.
-
Map.walk_plate_in_column_major_order(source.size) do |position, index|
-
next unless index < groups.size
-
destination_well = destination.wells.located_at(position.description).first or
-
raise StandardError, "The destination does not have a well at #{position.description}"
-
groups[index].each do |source|
-
sources_to_target[source] = destination_well unless should_well_not_be_transferred?(source)
-
end
-
end
-
end
-
end
-
1
private :well_to_destination
-
-
1
def record_transfer(source, destination)
-
self.transfers ||= {}
-
self.transfers[source.map.description] = destination.map.description
-
end
-
1
private :record_transfer
-
-
# Request type for transfers is based on the plates, not the wells we're transferring
-
1
def request_type_between(ignored_a, ignored_b)
-
destination.transfer_request_type_from(source)
-
end
-
1
private :request_type_between
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class Transfer::BetweenSpecificTubes < Transfer
-
1
include TransfersToKnownDestination
-
-
1
belongs_to :source, :polymorphic => true
-
-
1
after_create :update_destination_tube_name
-
1
def update_destination_tube_name
-
destination.update_attributes!(:name => source.name_for_child_tube)
-
end
-
1
private :update_destination_tube_name
-
-
1
def each_transfer(&block)
-
yield(source, destination)
-
end
-
1
private :each_transfer
-
-
1
def request_type_between(ignored_a, ignored_b)
-
destination.transfer_request_type_from(source)
-
end
-
1
private :request_type_between
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2015 Genome Research Ltd.
-
1
class Transfer::BetweenTubesBySubmission < Transfer
-
1
include TransfersToKnownDestination
-
-
1
belongs_to :source, :polymorphic => true
-
-
1
before_validation :ensure_destination_setup
-
1
def ensure_destination_setup
-
submission_id = source.submission.id
-
self.destination = source.stock_wells.flatten.first.requests_as_source.detect do |request|
-
request.library_creation? && request.submission_id == submission_id && request.target_tube
-
end.try(:target_tube)
-
end
-
1
private :ensure_destination_setup
-
-
1
after_create :update_destination_tube_name
-
1
def update_destination_tube_name
-
destination.update_attributes!(:name => source.name_for_child_tube)
-
end
-
1
private :update_destination_tube_name
-
-
1
def each_transfer(&block)
-
yield(source, destination)
-
end
-
1
private :each_transfer
-
-
1
def request_type_between(ignored_a, ignored_b)
-
destination.transfer_request_type_from(source)
-
end
-
1
private :request_type_between
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013 Genome Research Ltd.
-
1
module Transfer::BuildsStockWellLinks
-
1
def self.included(base)
-
2
base.class_eval do
-
2
after_create(:build_stock_well_relationships)
-
end
-
end
-
-
# The stock wells of the target well are either the source well if that well is on a stock plate,
-
# or they are the stock wells of our source well. We build from the source plate to avoid repeated
-
# creation of links on future transfers
-
1
def build_stock_well_relationships
-
stock_well_picker = source.plate_purpose.can_be_considered_a_stock_plate? ? ->(a) { [ a ] } : ->(a) { a.stock_wells }
-
eligable = destination.wells.map(&:id)
-
Hash.new {|h,v| h[v] = Array.new }.tap do |t|
-
source.wells.each do |well|
-
stock = stock_well_picker.call(well)
-
well.requests.where_is_a?(TransferRequest).each {|r| t[r.target_asset].concat(stock) if eligable.include?(r.target_asset_id) }
-
end
-
end.each do |well,stock_wells|
-
well.stock_wells.attach!(stock_wells)
-
end
-
end
-
1
private :build_stock_well_relationships
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class Transfer::FromPlateToSpecificTubes < Transfer::BetweenPlateAndTubes
-
1
attr_reader :targets
-
1
def targets=(uuids_for_tubes)
-
@targets = Uuid.lookup_many_uuids(uuids_for_tubes).map(&:resource)
-
end
-
-
1
def locate_mx_library_tube_for(well, stock_wells)
-
return nil if stock_wells.empty?
-
@tubes_to_pick ||= targets.dup
-
@pools_to_tubes ||= Hash.new { |h,k| h[k] = @tubes_to_pick.shift or raise "Not enough tubes to pick for pool" }
-
@pools_to_tubes[well.pool_id]
-
end
-
1
private :locate_mx_library_tube_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class Transfer::FromPlateToSpecificTubesByPool < Transfer::BetweenPlateAndTubes
-
1
attr_reader :targets
-
1
def targets=(uuids_for_tubes)
-
# {'pool_uuid'=>'target_uuid'}
-
@targets = Uuid.lookup_many_uuids(uuids_for_tubes.values).map(&:resource)
-
@pools_to_tubes = Hash.new
-
uuids_for_tubes.each do |pool_uuid,target_uuid|
-
@pools_to_tubes[Uuid.find_id(pool_uuid, 'Submission')] = Uuid.find_by_external_id(target_uuid).resource
-
end
-
end
-
-
1
def locate_mx_library_tube_for(well, stock_wells)
-
return nil if stock_wells.empty?
-
@pools_to_tubes[well.pool_id]
-
end
-
1
private :locate_mx_library_tube_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
# Picks the specified wells of a plate into an individual tube. In this case transfers is an
-
# array of well locations to transfer into the tube, and the destination is a tube.
-
1
class Transfer::FromPlateToTube < Transfer
-
1
include TransfersBySchema
-
1
include TransfersToKnownDestination
-
-
# The values in the transfers must be an array and must be valid well positions on the plate.
-
1
validates_each(:transfers) do |record, attribute, value|
-
if not value.is_a?(Array)
-
record.errors.add(:transfers, 'must be an array of well positions')
-
elsif record.source.present? and not record.source.valid_positions?(value)
-
record.errors.add(:transfers, 'are not valid positions on the source plate')
-
end
-
end
-
-
1
def each_transfer(&block)
-
# Partition the source plate wells into ones that are good and others that are bad. The
-
# bad wells will be eliminated after we've done the transfers for the good ones.
-
bad_wells, good_wells = source.wells.located_at_position(transfers).with_pool_id.partition do |well|
-
well.nil? or well.aliquots.empty? or well.failed? or well.cancelled?
-
end
-
-
good_wells.each { |well| yield(well, destination) }
-
-
# Eliminate any of the transfers that were not made because of the bad source wells
-
self.transfers = self.transfers - bad_wells.map { |well| well.map.description }
-
end
-
1
private :each_transfer
-
-
# Request type is based on the destination tube from the source plate
-
1
def request_type_between(_, destination)
-
destination.transfer_request_type_from(source)
-
end
-
1
private :request_type_between
-
-
1
after_create :update_tube_name
-
-
1
def update_tube_name
-
source_barcode = source.source_plate.try(:sanger_human_barcode)
-
range = "#{transfers.first}:#{transfers.last}"
-
destination.update_attributes!(:name=>"#{source_barcode} #{range}")
-
end
-
1
private :update_tube_name
-
-
-
end
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
# In contrast to pooling by submission, this method looks at submissions off the current
-
# plate. This allows users to use QC feedback to decide how to multiplex their plate.
-
1
class Transfer::FromPlateToTubeByMultiplex < Transfer::BetweenPlateAndTubes
-
-
1
def locate_mx_library_tube_for(well)
-
well.requests_as_source.where_is_a?(Request::Multiplexing).detect{|r| r.target_asset.aliquots.empty? }.try(:target_asset)
-
end
-
1
private :locate_mx_library_tube_for
-
-
1
def well_to_destination
-
@w2d||=ActiveSupport::OrderedHash[
-
source.wells.map do |well|
-
tube = locate_mx_library_tube_for(well)
-
(tube.nil? or should_well_not_be_transferred?(well)) ? nil : [ well, [ tube, tube.requests_as_target.map(&:asset) ] ]
-
end.compact
-
]
-
end
-
1
private :well_to_destination
-
-
-
# Before creating an instance of this class the appropriate transfers need to be made from a source
-
# asset to the destination one.
-
# before_create :create_transfer_requests
-
1
def create_transfer_requests
-
each_transfer do |source, destination|
-
request_type_between(source, destination).create!(
-
:asset => source,
-
:target_asset => destination,
-
:submission_id => destination.requests_as_target.first.submission_id
-
)
-
end
-
end
-
1
private :create_transfer_requests
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2015 Genome Research Ltd.
-
# At the end of the pulldown pipeline the wells of the final plate are transferred, individually,
-
# into MX library tubes. Each well is effectively a pool of the stock wells, once they've been
-
# through the pipeline, so the mapping needs to be based on the original submissions.
-
1
class Transfer::FromPlateToTubeBySubmission < Transfer::BetweenPlateAndTubes
-
1
def locate_mx_library_tube_for(well, stock_wells)
-
return nil if stock_wells.empty?
-
current_submission = well.creation_request.submission_id
-
stock_wells.first.requests_as_source.detect do |request|
-
request.submission_id == current_submission && request.target_asset.is_a?(Tube)
-
end.try(:target_asset)
-
end
-
1
private :locate_mx_library_tube_for
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
# Every request "moving" an asset from somewhere to somewhere else without really transforming it
-
# (chemically) as, cherrypicking, pooling, spreading on the floor etc
-
1
class TransferRequest < SystemRequest
-
-
1
module InitialTransfer
-
1
def perform_transfer_of_contents
-
target_asset.aliquots << asset.aliquots.map do |a|
-
aliquot = a.dup
-
aliquot.study_id = outer_request.initial_study_id
-
aliquot.project_id = outer_request.initial_project_id
-
aliquot
-
end unless asset.failed? or asset.cancelled?
-
end
-
1
private :perform_transfer_of_contents
-
-
1
def outer_request
-
asset.requests.detect{|r| r.library_creation? && r.submission_id == self.submission_id}
-
end
-
end
-
-
-
1
redefine_state_machine do
-
# The statemachine for transfer requests is more promiscuous than normal requests, as well
-
# as being more concise as it has fewer states.
-
-
1
aasm_column :state
-
1
aasm_initial_state :pending
-
-
1
aasm_state :pending
-
1
aasm_state :started
-
1
aasm_state :failed, :enter => :on_failed
-
1
aasm_state :passed
-
1
aasm_state :qc_complete
-
1
aasm_state :cancelled, :enter => :on_cancelled
-
-
# State Machine events
-
2
aasm_event :start do transitions :to => :started, :from => [:pending] end
-
2
aasm_event :pass do transitions :to => :passed, :from => [:pending, :started, :failed] end
-
2
aasm_event :fail do transitions :to => :failed, :from => [:pending, :started, :passed] end
-
2
aasm_event :cancel do transitions :to => :cancelled, :from => [:started, :passed, :qc_complete] end
-
2
aasm_event :cancel_before_started do transitions :to => :cancelled, :from => [:pending] end
-
2
aasm_event :detach do transitions :to => :pending, :from => [:pending] end
-
-
# Not all transfer quests will make this transition, but this way we push the
-
# decision back up to the pipeline
-
2
aasm_event :qc do transitions :to => :qc_complete, :from => [:passed] end
-
end
-
-
# Ensure that the source and the target assets are not the same, otherwise bad things will happen!
-
1
validate do |record|
-
if record.asset.present? and record.asset == record.target_asset
-
record.errors.add(:asset, 'cannot be the same as the target')
-
record.errors.add(:target_asset, 'cannot be the same as the source')
-
end
-
end
-
-
1
before_create(:add_request_type)
-
1
def add_request_type
-
self.request_type ||= RequestType.transfer
-
end
-
1
private :add_request_type
-
-
1
after_create(:perform_transfer_of_contents)
-
-
1
def perform_transfer_of_contents
-
target_asset.aliquots << asset.aliquots.map(&:dup) unless asset.failed? or asset.cancelled?
-
end
-
1
private :perform_transfer_of_contents
-
-
1
def on_failed
-
self.target_asset.remove_downstream_aliquots
-
end
-
1
private :on_failed
-
-
1
alias_method :on_cancelled, :on_failed
-
-
1
def remove_unused_assets
-
# Don't remove assets for transfer requests as they are made on creation
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# A template is effectively a partially constructed Transfer instance, containing only the
-
# transfers that should be made and the final Transfer class that should be constructed.
-
#
-
# For pulldown there are at least a couple of these templates:
-
# - several plate-to-plate transfers of various columns (but all rows)
-
# - one whole plate to tube transfer
-
class TransferTemplate < ActiveRecord::Base
-
include Uuid::Uuidable
-
-
# A name is a useful way to identify templates!
-
validates_presence_of :name
-
validates_uniqueness_of :name
-
-
# A template creates a particular Transfer subclass.
-
validates_presence_of :transfer_class_name
-
-
# A set of transfers that should be made.
-
serialize :transfers
-
-
def transfer_class
-
@transfer_class ||= transfer_class_name.constantize
-
end
-
-
def self.transfer_constructor(name)
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{name}(attributes)
-
attributes.merge!(:transfers => self.transfers) unless self.transfers.blank?
-
transfer_class.#{name}(attributes)
-
end
-
}, __FILE__, line)
-
end
-
-
transfer_constructor(:create!)
-
transfer_constructor(:preview!)
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012,2013,2014,2015 Genome Research Ltd.
-
class Tube < Aliquot::Receptacle
-
include LocationAssociation::Locatable
-
include Barcode::Barcodeable
-
include ModelExtensions::Tube
-
include Tag::Associations
-
include Asset::Ownership::Unowned
-
include Transfer::Associations
-
include Transfer::State::TubeState
-
-
extend QcFile::Associations
-
has_qc_files
-
-
# Transfer requests into a tube are direct requests where the tube is the target.
-
def transfer_requests
-
requests_as_target.where_is_a?(TransferRequest).all
-
end
-
-
def automatic_move?
-
true
-
end
-
-
def subject_type
-
'tube'
-
end
-
-
has_many :submissions, :through => :requests_as_target, :uniq =>true
-
scope :include_scanned_into_lab_event, -> { includes(:scanned_into_lab_event) }
-
-
scope :with_purpose, ->(*purposes) {
-
where(:plate_purpose_id => purposes.flatten.map(&:id))
-
}
-
-
def submission
-
submissions.first
-
end
-
-
def ancestor_of_purpose(ancestor_purpose_id)
-
return self if self.plate_purpose_id == ancestor_purpose_id
-
ancestors.first(:order => 'created_at DESC', :conditions => {:plate_purpose_id=>ancestor_purpose_id})
-
end
-
-
def original_stock_plates
-
ancestors.find(:all,:conditions => {:plate_purpose_id => PlatePurpose.stock_plate_purpose })
-
end
-
-
alias_method :friendly_name, :sanger_human_barcode
-
-
# Base class for the all tube purposes
-
class Purpose < ::Purpose
-
# TODO: change to purpose_id
-
has_many :tubes, :foreign_key => :plate_purpose_id
-
-
# def default_state(_=nil)
-
# self[:default_state]
-
# end
-
-
# Tubes of the general types have no stock plate!
-
def stock_plate(_)
-
nil
-
end
-
-
def library_source_plates(_)
-
[]
-
end
-
-
def created_with_request_options(tube)
-
tube.creation_request.try(:request_options_for_creation) || {}
-
end
-
-
def create!(*args, &block)
-
target_class.create_with_barcode!(*args, &block).tap { |t| tubes << t }
-
end
-
-
def sibling_tubes(tube)
-
nil
-
end
-
-
# Define some simple helper methods
-
class << self
-
[ 'stock', 'standard' ].each do |purpose_type|
-
[ 'sample', 'library', 'MX' ].each do |tube_type|
-
name = "#{purpose_type} #{tube_type}"
-
-
line = __LINE__ + 1
-
class_eval(%Q{
-
def #{name.downcase.gsub(/\W+/, '_')}_tube
-
find_by_name('#{name.humanize}') or raise "Cannot find #{name} tube"
-
end
-
}, __FILE__, line)
-
end
-
end
-
end
-
end
-
-
class StockMx < Tube::Purpose
-
def transition_to(tube, state, user, _ = nil, customer_accepts_responsibility=false)
-
tube.requests_as_target.opened.each do |request|
-
request.transition_to(state)
-
end
-
end
-
-
def pool_id(tube)
-
tube.submission.try(:id)
-
end
-
-
def name_for_child_tube(tube)
-
tube.name
-
end
-
end
-
-
class StandardMx < Tube::Purpose
-
def created_with_request_options(tube)
-
tube.parent.try(:created_with_request_options)||{}
-
end
-
-
# Transitioning an MX library tube to a state involves updating the state of the transfer requests. If the
-
# state is anything but "started" or "pending" then the pulldown library creation request should also be
-
# set to the same state
-
def transition_to(tube, state, user, _ = nil, customer_accepts_responsibility=false)
-
update_all_requests = ![ 'started', 'pending' ].include?(state)
-
tube.requests_as_target.opened.for_billing.each do |request|
-
request.transition_to(state) if update_all_requests or request.is_a?(TransferRequest)
-
end
-
end
-
end
-
-
def self.delegate_to_purpose(*methods)
-
methods.each do |method|
-
class_eval(%Q{def #{method}(*args, &block) ; purpose.#{method}(self, *args, &block) ; end})
-
end
-
end
-
-
# TODO: change column name to account for purpose, not plate_purpose!
-
belongs_to :purpose, :class_name => 'Tube::Purpose', :foreign_key => :plate_purpose_id
-
1
delegate_to_purpose(:transition_to, :created_with_request_options, :pool_id, :name_for_child_tube, :stock_plate)
-
delegate :barcode_type, :to => :purpose
-
-
1
def name_for_label
-
(primary_aliquot.nil? or primary_aliquot.sample.sanger_sample_id.blank?) ? self.name : primary_aliquot.sample.shorten_sanger_sample_id
-
end
-
-
1
def details
-
purpose.try(:name)||'Tube'
-
end
-
-
1
def transfer_request_type_from(source)
-
purpose.transfer_request_type_from(source.purpose)
-
end
-
-
-
1
def self.create_with_barcode!(*args, &block)
-
attributes = args.extract_options!
-
barcode = args.first || attributes[:barcode]
-
raise "Barcode: #{barcode} already used!" if barcode.present? and find_by_barcode(barcode).present?
-
barcode ||= AssetBarcode.new_barcode
-
create!(attributes.merge(:barcode => barcode), &block)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014 Genome Research Ltd.
-
1
class TubeCreation < AssetCreation
-
1
class ChildTube < ActiveRecord::Base
-
1
self.table_name =('tube_creation_children')
-
1
belongs_to :tube_creation
-
1
belongs_to :tube
-
end
-
-
1
belongs_to :parent, :class_name => 'Plate'
-
1
include_plate_named_scope :parent
-
-
1
has_many :child_tubes, :class_name => 'TubeCreation::ChildTube'
-
1
has_many :children, :through => :child_tubes, :source => :tube
-
-
1
validates_each(:parent, :unless => :no_pooling_expected?, :allow_blank => true) do |record, attr, value|
-
record.errors.add(:parent, 'has no pooling information') if record.parent.pools.empty?
-
end
-
-
1
def no_pooling_expected?
-
parent_nil?
-
end
-
1
private :no_pooling_expected?
-
-
1
def target_for_ownership
-
children
-
end
-
1
private :target_for_ownership
-
-
1
def create_children!
-
self.children = (1..parent.pools.size).map { |_| child_purpose.create! }
-
end
-
1
private :create_children!
-
-
1
def create_ancestor_plate!
-
children.each do |child|
-
create_ancestor_asset!(parent.plate, child) if can_create_ancestor_plate?(parent.plate, child)
-
end
-
end
-
1
before_save :create_ancestor_plate!
-
-
-
1
def record_creation_of_children
-
# children.each { |child| parent.events.create_tube!(child_purpose, child, user) }
-
end
-
1
private :record_creation_of_children
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class TubeFromTubeCreation < AssetCreation
-
-
1
belongs_to :child, :class_name => 'Tube'
-
1
belongs_to :parent, :class_name => 'Tube'
-
-
1
def target_for_ownership
-
child
-
end
-
1
private :target_for_ownership
-
-
1
def children
-
[self.child]
-
end
-
1
private :children
-
-
1
def create_children!
-
self.child = child_purpose.create!
-
end
-
1
private :create_children!
-
-
1
def record_creation_of_children
-
# children.each { |child| parent.events.create_tube!(child_purpose, child, user) }
-
end
-
1
private :record_creation_of_children
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module UiHelper
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
-
# Ideally we'd convert this into a scope/association, but its complicated by the need to associate across
-
# two models, one of which we're trying to deprecate.
-
1
require 'will_paginate/array'
-
-
1
module UiHelper
-
1
class Summary
-
1
attr_accessor :summary_items
-
1
attr_accessor :current_page
-
-
1
def initialize(options = {})
-
@summary_items = []
-
@current_page = options[:page].to_i || 1
-
@item_per_page = options[:per_page].to_i || 10
-
end
-
-
1
def load(study, workflow)
-
study.submissions_for_workflow(workflow).each do |submission|
-
submission.items.each do |item|
-
self.load_item(item)
-
end
-
end
-
self.load_study_item(study)
-
self.get_items
-
end
-
-
1
def size
-
self.summary_items.size
-
end
-
-
1
def load_item(asset)
-
asset.requests.map { |r| r.events }.flatten.each do |event|
-
if event.message && event.message.match(/Run/)
-
self.add(SummaryItem.new({:message => "#{event.message}",
-
:object => event.eventful,
-
:timestamp => event.created_at,
-
:external_message => "Run #{event.identifier}",
-
:external_link => "#{configatron.run_information_url}#{event.identifier}"}))
-
end
-
end
-
end
-
-
1
def load_study_item(study)
-
study.events.each do |event|
-
self.add(SummaryItem.new({:message => "#{event.message}",
-
:object => study,
-
:timestamp => event.created_at,
-
:external_message => "Study #{study.id}",
-
:external_link => ""}))
-
end
-
end
-
-
1
def get_items
-
self.summary_items.sort{ |a,b| b.timestamp <=> a.timestamp }
-
end
-
-
1
def add(item)
-
@summary_items << item
-
end
-
-
1
def size
-
@summary_items.size
-
end
-
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module UiHelper
-
1
class SummaryItem
-
1
attr_accessor :message
-
1
attr_accessor :object
-
1
attr_accessor :timestamp
-
-
1
attr_accessor :external_link
-
1
attr_accessor :external_message
-
-
1
def initialize(options = {})
-
@message = options[:message]
-
@object = options[:object]
-
@timestamp = options[:timestamp]
-
@external_link = options[:external_link]
-
@external_message = options[:external_message]
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013,2014,2015 Genome Research Ltd.
-
1
class UnrepeatableSequencingPipeline < SequencingPipeline
-
-
1
def request_actions
-
[:fail]
-
end
-
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
1
require "net/ldap"
-
1
require "openssl"
-
1
require "digest/sha1"
-
#require 'curb'
-
-
1
class User < ActiveRecord::Base
-
1
include Authentication
-
1
include Workflowed
-
1
extend EventfulRecord
-
1
include Uuid::Uuidable
-
1
include Swipecardable
-
1
has_many_events
-
-
1
has_many :lab_events
-
1
has_many :items
-
1
has_many :requests
-
1
has_many :comments
-
1
has_many :settings
-
1
has_many :roles
-
1
has_many :submissions
-
1
has_many :project_roles, :class_name => 'Role', :conditions => {authorizable_type:'Project'}
-
1
has_many :study_roles, :class_name => 'Role', :conditions => {authorizable_type:'Study'}
-
1
has_many :study_roles
-
1
has_many :batches
-
1
has_many :assigned_batches, :class_name => 'Batch', :foreign_key => :assignee_id, :inverse_of => :assignee
-
1
has_many :pipelines, :through => :batches, :order => 'batches.id DESC', :uniq => true
-
-
1
before_save :encrypt_password
-
3
before_create { |record| record.new_api_key if record.api_key.blank? }
-
3
before_create { |record| record.workflow ||= Submission::Workflow.default_workflow }
-
-
1
validates_presence_of :login
-
1
validates_confirmation_of :password, :if => :password_required?
-
-
1
scope :with_login, ->(*logins) { { :conditions => { :login => logins.flatten } } }
-
1
scope :all_administrators, -> { joins(:roles).where(:roles=>{:name=>'administrator'}) }
-
-
1
acts_as_authorized_user
-
-
1
scope :owners, ->() { where('last_name IS NOT NULL').joins(:roles).where(:roles=>{:name=>'owner'}).order('last_name ASC').uniq }
-
-
1
attr_accessor :password
-
-
1
def self.prefix
-
'ID'
-
end
-
-
1
def study_roles
-
self.user_roles("Study")
-
end
-
-
1
def project_roles
-
self.user_roles("Project")
-
end
-
-
1
def study_and_project_roles
-
study_roles | project_roles
-
end
-
-
1
def user_roles(authorizable_class_name)
-
roles.where(authorizable_type:authorizable_class_name)
-
end
-
-
1
def following?(item)
-
self.has_role? 'follower', item
-
end
-
-
1
def logout_path
-
if configatron.authentication == "sanger-sso"
-
return "#{configatron.sso_logout_url}"
-
else
-
return "/logout"
-
end
-
end
-
-
1
def profile_incomplete?
-
name_incomplete? or email.blank? or swipecard_code.blank?
-
end
-
-
1
def profile_complete?
-
not profile_incomplete?
-
end
-
-
1
def name_incomplete?
-
1
first_name.blank? or last_name.blank?
-
end
-
-
1
def name_complete?
-
not name_incomplete?
-
end
-
-
1
def name
-
1
name_incomplete? ? self.login : "#{self.first_name} #{self.last_name}"
-
end
-
-
1
def projects
-
# We use where(true) to get a scope. In Later versions of rails all is a scope
-
return Project.where(true) if self.is_administrator?
-
atuhorized = authorized_projects
-
return Project.where(true) if ( (atuhorized.blank?) && (privileged?) )
-
atuhorized
-
end
-
-
1
def authorized_projects
-
Project.for_user(self)
-
end
-
-
1
def sorted_project_names_and_ids
-
projects.alphabetical.map{|p| [p.name, p.id] }
-
end
-
-
1
def sorted_valid_project_names_and_ids
-
valid_projects.map{|p| [p.name, p.id] }
-
end
-
-
1
def valid_projects
-
projects.valid.alphabetical
-
end
-
-
-
1
def sorted_study_names_and_ids
-
interesting_studies.alphabetical.map{|p| [p.name, p.id] }
-
end
-
-
1
def workflow_name
-
1
self.workflow and self.workflow.name
-
end
-
-
1
def has_preference_for(key)
-
setting_for?(key)
-
end
-
-
1
def privileged?(item=nil)
-
privileged = false
-
privileged = true if manager_or_administrator?
-
unless item.nil?
-
privileged = true if self.owner?(item)
-
end
-
privileged
-
end
-
-
1
def internal?
-
self.has_role? 'internal'
-
end
-
-
1
def qa_manager?
-
self.has_role? 'qa_manager'
-
end
-
-
1
def lab_manager?
-
self.has_role? 'lab_manager'
-
end
-
-
1
def slf_manager?
-
self.has_role? 'slf_manager'
-
end
-
-
1
def slf_gel?
-
self.has_role? 'slf_gel'
-
end
-
-
1
def lab?
-
self.has_role? 'lab'
-
end
-
-
1
def owner?(item)
-
self.has_role? 'owner', item
-
end
-
-
1
def manager_or_administrator?
-
self.is_administrator? || self.is_manager?
-
end
-
-
1
def manager?
-
is_manager?
-
end
-
-
1
def administrator?
-
is_administrator?
-
end
-
-
# returns emails of all admins
-
1
def self.all_administrators_emails
-
self.all_administrators.map(&:email).compact.uniq
-
end
-
-
# Encrypts some data with the salt.
-
1
def self.encrypt(password, salt)
-
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
-
end
-
-
1
def new_api_key(length = 32)
-
u = Digest::SHA1.hexdigest(self.login)[0..12]
-
k = Digest::SHA1.hexdigest(Time.now.to_s + rand(12341234).to_s)[1..length]
-
self.api_key = "#{u}-#{k}"
-
end
-
-
# Encrypts the password with the user salt
-
1
def encrypt(password)
-
self.class.encrypt(password, salt)
-
end
-
-
1
def remember_token?
-
remember_token_expires_at && Time.now.utc < remember_token_expires_at
-
end
-
-
# These create and unset the fields required for remembering users between browser closes
-
1
def remember_me
-
self.remember_token_expires_at = 2.weeks.from_now.utc
-
self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")
-
save(:validate => false)
-
end
-
-
1
def forget_me
-
self.remember_token_expires_at = nil
-
self.remember_token = nil
-
save(:validate => false)
-
end
-
-
# User has a relationship by role to these studies
-
1
def interesting_studies
-
Study.of_interest_to(self)
-
end
-
-
1
def self.valid_barcode?(code)
-
begin
-
human_code = Barcode.barcode_to_human!(code, self.prefix)
-
rescue
-
return false
-
end
-
return false unless User.find_by_barcode(human_code)
-
-
true
-
end
-
-
1
def self.lookup_by_barcode(user_barcode)
-
barcode = Barcode.barcode_to_human(user_barcode)
-
if barcode
-
return User.find_by_barcode(barcode)
-
end
-
-
nil
-
end
-
-
1
protected
-
# before filter
-
1
def encrypt_password
-
2
return if password.blank?
-
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
-
self.crypted_password = encrypt(password)
-
end
-
-
1
def password_required?
-
2
crypted_password.blank? || !password.blank?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013 Genome Research Ltd.
-
1
module User::Authentication
-
1
def self.included(base)
-
1
base.class_eval do
-
1
extend ClassMethods
-
1
extend Ldap
-
1
extend Local
-
end
-
end
-
-
1
def authenticated?(password)
-
crypted_password == encrypt(password)
-
end
-
-
1
def update_profile_via_ldap
-
ldap = Net::LDAP.new(:host => configatron.ldap_server, :port => configatron.ldap_port)
-
-
filter = Net::LDAP::Filter.eq( "uid", self.login )
-
treebase = "ou=people,dc=sanger,dc=ac,dc=uk"
-
-
ldap_profile = ldap.search( :base => treebase, :filter => filter )[0]
-
# If we have two or more records, something is off with LDAP
-
-
{:email => "mail", :first_name => "givenname", :last_name => "sn"}.each do |attr,ldap_attr|
-
self[attr] = ldap_profile[ldap_attr][0] if self[attr].blank?
-
end
-
self.save if self.changed?
-
-
rescue StandardError => e
-
logger.error "Profile failed for user #{login}: result code #{ldap.get_operation_result.code} message #{ldap.get_operation_result.message} - #{e}"
-
end
-
1
private :update_profile_via_ldap
-
-
1
module ClassMethods
-
# Authenticates a user by their login name and unencrypted password. Returns the user or nil.
-
1
def authenticate(login, password)
-
if configatron.authentication == "ldap"
-
authenticated = authenticate_with_ldap(login, password)
-
else
-
authenticated = authenticate_by_local(login, password)
-
end
-
if authenticated
-
u = find_or_create_by_login(login)
-
if u.nil?
-
logger.error "Failed to find or create user #{login}"
-
else
-
u.send(:update_profile_via_ldap) unless u.profile_complete?
-
end
-
u
-
else
-
nil
-
end
-
end
-
end
-
-
1
module Ldap
-
1
def authenticate_with_ldap(login, password)
-
# TODO - Extract LDAP specifics to configuration
-
username = "uid=" << login << ",ou=people,dc=sanger,dc=ac,dc=uk"
-
ldap = Net::LDAP.new(
-
:host => configatron.ldap_server,
-
:port => configatron.ldap_secure_port,
-
:encryption => :simple_tls,
-
:auth => {
-
:method => :simple,
-
:username => username,
-
:password => password
-
}
-
)
-
begin
-
ldap.bind
-
rescue StandardError => e
-
raise e, "LDAP connection problem: #{e}", caller
-
end
-
password = "" # clear out in case of crashes
-
if ldap.bind
-
logger.info "Authentication succeeded"
-
true
-
else
-
logger.warn "Authentication failed for user #{login}: result code #{ldap.get_operation_result.code} message #{ldap.get_operation_result.message}"
-
false
-
end
-
end
-
end
-
-
1
module Local
-
1
def authenticate_by_local(login, password)
-
u = find_by_login(login) # need to get the salt
-
u && u.authenticated?(password) ? u : nil
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
1
class Uuid < ActiveRecord::Base
-
1
module Uuidable
-
1
def self.included(base)
-
49
base.class_eval do
-
# We need to add some class level changes to this model because the ar-extensions gem might be
-
# used.
-
#extend ArExtensionsFix
-
-
# Ensure that the resource has a UUID and that it's always created when the instance is created.
-
# It seems better not to do this but the performance of the API is directly affected by having to
-
# create these instances when they do not exist.
-
49
has_one :uuid_object, :class_name => 'Uuid', :as => :resource, :dependent => :destroy, :inverse_of => :resource
-
49
after_create :ensure_uuid_created
-
-
# Some named scopes ...
-
49
scope :include_uuid, -> { includes(:uuid_object ) }
-
end
-
end
-
-
# In the test environment we need to have a slightly different behaviour, as we can predefine
-
# the UUID for a record to make things predictable. In production new records always have new
-
# UUIDs.
-
1
if ['test', 'cucumber'].include?(Rails.env)
-
1
def ensure_uuid_created
-
10
self.create_uuid_object!(:resource => self) unless self.uuid_object(true).present?
-
end
-
else
-
def ensure_uuid_created
-
self.create_uuid_object!(:resource => self) || raise(ActiveRecord::RecordInvalid) # = Uuid.create!(:resource => self)
-
end
-
end
-
1
private :ensure_uuid_created
-
-
# Marks a record as being unsaved and hence the UUID is not present. This is not something we
-
# want to actually happen without being explicitly told; hence, the 'uuid' method below will
-
# error if the record is unsaved as that's exactly what should happen.
-
#
-
# It also means that marking a record by calling this method, and then attempting to save it,
-
# will result in another validation exception. Again, exactly what we want.
-
1
def unsaved_uuid!
-
self.uuid_object = Uuid.new(:external_id => nil)
-
end
-
-
#--
-
# You cannot give a UUID to something that hasn't been saved, which means that the UUID can't be
-
# relied on to actually be present, nor can it be relied on to be output into any JSON in the API.
-
#++
-
1
def uuid
-
(self.uuid_object || self.create_uuid_object).uuid
-
end
-
-
# The behaviour of the ar-extensions gem means that the after_create callbacks aren't being executed
-
# for any of the rows we create on import. What we need to do is ensure that any rows in the table
-
# that do not have UUIDs have their UUIDs created, but we also need to do this unobtrusively.
-
1
module ArExtensionsFix
-
1
def self.extended(base)
-
base.singleton_class.alias_method_chain(:import, :uuid_creation)
-
end
-
-
1
def import_with_uuid_creation(*args, &block)
-
import_without_uuid_creation(*args, &block).tap do |results|
-
generate_missing_uuids unless results.num_inserts.zero?
-
end
-
end
-
-
1
def generate_missing_uuids
-
records_for_missing_uuids { |id| Uuid.create!(:resouce_type=>self.name, :resource_id=>id, :external_id=>Uuid.generate_uuid ) }
-
end
-
1
private :generate_missing_uuids
-
-
1
def records_for_missing_uuids(&block)
-
self.connection.select_all(%Q{
-
SELECT r.id AS id
-
FROM #{self.quoted_table_name} r
-
LEFT OUTER JOIN #{Uuid.quoted_table_name} u
-
ON r.id=u.resource_id AND u.resource_type="#{self.to_s}"
-
WHERE u.id IS NULL
-
}).map { |r| block.call(r['id']) }
-
end
-
1
private :records_for_missing_uuids
-
end
-
end
-
-
1
ValidRegexp = /^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$/
-
1
validates_format_of :external_id, :with => ValidRegexp
-
-
# It is more efficient to check the individual parts of the resource association than it is to check the
-
# association itself as the latter causes the record to be reloaded
-
1
belongs_to :resource, :polymorphic => true, :inverse_of => :uuid_object
-
-
# TODO[xxx]: remove this and use resource everywhere!
-
1
def object
-
self.resource
-
end
-
-
1
scope :with_resource_type, ->(type) { where(:resource_type => type.to_s ) }
-
-
1
scope :include_resource, -> { includes(:resource) }
-
1
scope :with_external_id, ->(external_id) { where(:external_id => external_id) }
-
1
scope :with_resource_by_type_and_id, ->(t,id) { where(:resource_type => t, :resource_id => id ) }
-
-
1
before_validation do |record|
-
10
record.external_id = Uuid.generate_uuid if record.new_record? and record.external_id.blank?
-
end
-
-
1
def uuid
-
self.external_id
-
end
-
-
1
def self.generate_uuid
-
10
UUIDTools::UUID.timestamp_create.to_s
-
end
-
-
1
def self.translate_uuids_to_ids_in_params(params)
-
params.keys.each do |key|
-
next unless params[key] =~ ValidRegexp
-
params[key] = self.find_id(params[key])
-
end
-
end
-
-
1
def self.find_uuid_instance!(resource_type, resource_id)
-
self.with_resource_by_type_and_id(resource_type, resource_id).first or
-
raise ActiveRecord::RecordNotFound, "Unable to find UUID"
-
end
-
-
# Find the uuid corresponding id and system.
-
# @param resource_name [String] the name of the external project
-
# @param id [String, Integer ]
-
# @return [String, nil] the uuid if found.
-
-
1
def self.find_uuid(resource_type, resource_id)
-
begin
-
find_uuid_instance!(resource_type, resource_id).external_id
-
rescue ActiveRecord::RecordNotFound => exception
-
return nil
-
end
-
end
-
-
# Find an Uuid or create it if needed.
-
# @param resource_name [String] the name of the external project
-
# @param id [String, Integer ]
-
# @return [String] the uuid .
-
1
def self.find_uuid!(resource_type, resource_id)
-
return unless id # return nil for nil
-
find_uuid(resource_type, resource_id) ||
-
create!(:resource_type => resource_type, :resource_id => resource_id).external_id
-
end
-
-
-
# Given a list of internal ids, create uuids in bulk
-
# @param resource_name [String] the name of the external project
-
# @param base_class_name [String] the basic type in the external project.
-
# @param id [String, Integer ]
-
# @return [String] the uuid .
-
1
def self.generate_uuids!(resource_type, resource_ids)
-
return if resource_ids.empty?
-
ids_missing_uuids = filter_uncreated_uuids(resource_type, resource_ids)
-
uuids_to_create = ids_missing_uuids.map {|id| create!(:resource_type => resource_type, :resource_id => id, :external_id => self.generate_uuid) }
-
#Uuid.import uuids_to_create unless uuids_to_create.empty?
-
-
nil
-
end
-
-
# ids is a string of internal_ids
-
1
def self.filter_uncreated_uuids(resource_type, resource_ids)
-
existing_uuids = all(:conditions => { :resource_type => resource_type, :resource_id => resource_ids })
-
resource_ids - existing_uuids.map(&:resource_id)
-
end
-
-
1
def self.generate_all_uuids_for_class(base_class_name)
-
eval(base_class_name).find_in_batches(:batch_size => 5000) do |group|
-
generate_uuids!(base_class_name.to_s, group.map(&:id))
-
end
-
end
-
-
# Find the id corresponding to the uuid. Check the resource and base_class names are as expected if they are given.
-
# @param uuid [String]
-
# @param resource_name [String] the name of the external project
-
# @return [String, nil]
-
# @raise Response::Exception if system doesn't macth.
-
1
def self.find_id(uuid, resource_type=nil)
-
begin
-
uuid_object = with_external_id(uuid).first or raise ActiveRecord::RecordNotFound, "Could not find UUID #{uuid.inspect}"
-
-
Response::InvalidUuid.throw_new(uuid) if resource_type && resource_type != uuid_object.resource_type
-
-
uuid_object.resource_id
-
rescue
-
return nil
-
end
-
end
-
-
-
1
class << self
-
1
def lookup_single_uuid(uuid)
-
with_external_id(uuid).first or
-
raise ActiveRecord::RecordNotFound, "Could not find UUID #{uuid.inspect}"
-
end
-
-
1
def lookup_many_uuids(uuids)
-
with_external_id(uuids).all.tap do |found|
-
missing = uuids - found.map(&:external_id)
-
raise ActiveRecord::RecordNotFound, "Could not find UUIDs #{missing.map(&:inspect).join(',')}" unless missing.empty?
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class ValidateSampleSheetTask < Task
-
1
class ValidateSampleSheetData < Task::RenderElement
-
1
def initialize(request)
-
super(request)
-
end
-
end
-
-
1
def create_render_element(request)
-
request.asset && ValidateSampleSheetData.new(request)
-
end
-
-
1
def partial
-
"validate_sample_sheet_batches"
-
end
-
-
1
def render_task(workflow, params)
-
super
-
workflow.render_validate_sample_sheet_task(self, params)
-
end
-
-
1
def do_task(workflow, params)
-
workflow.do_validate_sample_sheet_task(self, params)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011 Genome Research Ltd.
-
# It's such a common pattern in the code to have a guard on some our validations that this module provides
-
# that facility. You can declare a guard with:
-
#
-
# extend ValidationStateGuard
-
# validation_guard(:guard_name)
-
#
-
# This then gives you *private* methods of guard_name, guard_name=, and guard_name? It also adds an after_save
-
# callback that ensures that guard_name is false so it doesn't leak.
-
#
-
# If you have a method that should enable a guard and then disable it afterwards then you can do:
-
#
-
# validation_guarded_by(:method_that_needs_guard_enabled, :guard_name)
-
#
-
# This will set the guard_name to true before the method is called and return it to false afterwards.
-
module ValidationStateGuard
-
def validation_guard(guard)
-
guard = guard.to_sym
-
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
attr_accessor #{guard.inspect}
-
1
alias_method(#{guard.inspect}?, #{guard.inspect})
-
1
private #{guard.inspect}, #{guard.inspect}?
-
1
protected #{guard.inspect}=
-
-
# Do not remove the 'true' from this otherwise the return value is false, which will fail the save!
-
42
after_save { |record| record.send(#{guard.inspect}=, false) ; true }
-
}, __FILE__, line)
-
end
-
-
def validation_guarded_by(method, guard)
-
# Method name could end in ! or ?, in which case the unguarded name needs to be correct.
-
method.to_s =~ /^([^!?]+)([!?])?$/
-
core_name, extender = $1, $2
-
unguarded_name = :"#{core_name}_unguarded_by_#{guard}#{extender}"
-
-
line = __LINE__ + 1
-
class_eval(%Q{
-
alias_method(#{unguarded_name.inspect}, #{method.to_sym.inspect})
-
def #{method}(*args, &block)
-
send(#{guard.to_sym.inspect}=, true)
-
#{unguarded_name}(*args, &block)
-
ensure
-
send(#{guard.to_sym.inspect}=, false)
-
end
-
}, __FILE__, line)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012,2013,2014,2015 Genome Research Ltd.
-
class Well < Aliquot::Receptacle
-
include Api::WellIO::Extensions
-
include ModelExtensions::Well
-
include Cherrypick::VolumeByNanoGrams
-
include Cherrypick::VolumeByNanoGramsPerMicroLitre
-
include Cherrypick::VolumeByMicroLitre
-
include StudyReport::WellDetails
-
include Tag::Associations
-
include AssetRack::WellAssociations::AssetRackAssociation
-
include Api::Messages::FluidigmPlateIO::WellExtensions
-
-
class Link < ActiveRecord::Base
-
self.table_name = 'well_links'
-
self.inheritance_column = nil
-
belongs_to :target_well, :class_name => 'Well'
-
belongs_to :source_well, :class_name => 'Well'
-
end
-
has_many :stock_well_links, :class_name => 'Well::Link', :foreign_key => :target_well_id, :conditions => { :type => 'stock' }
-
-
has_many :stock_wells, :through => :stock_well_links, :source => :source_well do
-
def attach!(wells)
-
attach(wells).tap do |_|
-
proxy_association.owner.save!
-
end
-
end
-
def attach(wells)
-
proxy_association.owner.stock_well_links.build(wells.map { |well| { :type => 'stock', :source_well => well } })
-
end
-
end
-
-
has_many :qc_metrics, :inverse_of => :asset, :foreign_key => :asset_id
-
-
def is_in_fluidigm?
-
sta_plate_purpose_name = "#{configatron.sta_plate_purpose_name}"
-
!self.target_wells.detect{|w| w.events.detect {|e| e.family == PlatesHelper::event_family_for_pick(sta_plate_purpose_name)}.nil?}
-
end
-
-
scope :include_stock_wells, -> { includes(:stock_wells => :requests_as_source) }
-
scope :include_map, -> { includes(:map) }
-
-
scope :located_at, ->(location) {
-
joins(:map).where(:maps => { :description => location })
-
}
-
-
scope :on_plate_purpose, ->(purposes) {
-
joins(:plate).
-
where(:plates_assets=>{:plate_purpose_id=>purposes})
-
}
-
-
scope :for_study_through_sample, ->(study) {
-
joins(:aliquots=>{:sample=>:study_samples}).
-
where(:study_samples=>{:study_id=>study})
-
}
-
-
scope :for_study_through_aliquot, ->(study) {
-
joins(:aliquots).
-
where(:aliquots=>{:study_id=>study})
-
}
-
-
#
-
scope :without_report, ->(product_criteria) {
-
joins([
-
'LEFT OUTER JOIN qc_metrics AS wr_qcm ON wr_qcm.asset_id = assets.id',
-
'LEFT OUTER JOIN qc_reports AS wr_qcr ON wr_qcr.id = wr_qcm.qc_report_id',
-
'LEFT OUTER JOIN product_criteria AS wr_pc ON wr_pc.id = wr_qcr.product_criteria_id'
-
]).
-
group('assets.id').
-
having("NOT BIT_OR(wr_pc.product_id = ? AND wr_pc.stage = ?)",product_criteria.product_id,product_criteria.stage)
-
}
-
-
has_many :target_well_links, :class_name => 'Well::Link', :foreign_key => :source_well_id, :conditions => { :type => 'stock' }
-
has_many :target_wells, :through => :target_well_links, :source => :target_well
-
-
scope :stock_wells_for, ->(wells) { {
-
:joins => :target_well_links,
-
:conditions => {
-
:well_links =>{
-
:target_well_id => [wells].flatten.map(&:id)
-
}
-
}
-
}}
-
-
scope :located_at_position, ->(position) { joins(:map).readonly(false).where(:maps => { :description => position }) }
-
-
contained_by :plate
-
-
# We don't handle this in contained by as identifiable pieces of labware
-
# may still be contained. (Such as if we implement tube racks)
-
def labware
-
plate
-
1
end
-
-
delegate :location, :location_id, :location_id=, :to => :container , :allow_nil => true
-
1
self.per_page = 500
-
-
1
has_one :well_attribute, :inverse_of => :well
-
1
before_create { |w| w.create_well_attribute unless w.well_attribute.present? }
-
-
1
scope :pooled_as_target_by, ->(type) {
-
joins('LEFT JOIN requests patb ON assets.id=patb.target_asset_id').
-
where([ '(patb.sti_type IS NULL OR patb.sti_type IN (?))', [ type, *type.descendants ].map(&:name) ]).
-
select('DISTINCT assets.*, patb.submission_id AS pool_id')
-
}
-
1
scope :pooled_as_source_by, ->(type) {
-
joins('LEFT JOIN requests pasb ON assets.id=pasb.asset_id').
-
where([ '(pasb.sti_type IS NULL OR pasb.sti_type IN (?)) AND pasb.state IN (?)', [ type, *type.descendants ].map(&:name), Request::Statemachine::OPENED_STATE ]).
-
select('DISTINCT assets.*, pasb.submission_id AS pool_id')
-
}
-
1
scope :in_column_major_order, -> { joins(:map).order('column_order ASC') }
-
1
scope :in_row_major_order, -> { joins(:map).order('row_order ASC') }
-
1
scope :in_inverse_column_major_order, -> { joins(:map).order('column_order DESC') }
-
1
scope :in_inverse_row_major_order, -> { joins(:map).order('row_order DESC') }
-
-
1
scope :in_plate_column, ->(col,size) { joins(:map).where(:maps => {:description => Map::Coordinate.descriptions_for_column(col,size), :asset_size => size }) }
-
1
scope :in_plate_row, ->(row,size) { joins(:map).where(:maps => {:description => Map::Coordinate.descriptions_for_row(row,size), :asset_size =>size }) }
-
-
1
scope :with_blank_samples, -> {
-
joins([
-
"INNER JOIN aliquots ON aliquots.receptacle_id=assets.id",
-
"INNER JOIN samples ON aliquots.sample_id=samples.id"
-
]).
-
where(['samples.empty_supplier_sample_name=?',true])
-
}
-
-
1
scope :without_blank_samples, ->() {
-
joins(:aliquots=>:sample).
-
where(:samples => { :empty_supplier_sample_name=> false })
-
}
-
-
1
scope :with_contents, -> {
-
joins('INNER JOIN aliquots ON aliquots.receptacle_id=assets.id')
-
}
-
-
1
class << self
-
1
def delegate_to_well_attribute(attribute, options = {})
-
12
class_eval <<-END_OF_METHOD_DEFINITION
-
12
def get_#{attribute}
-
12
self.well_attribute.#{attribute} || #{options[:default].inspect}
-
end
-
END_OF_METHOD_DEFINITION
-
end
-
-
1
def writer_for_well_attribute_as_float(attribute)
-
6
class_eval <<-END_OF_METHOD_DEFINITION
-
6
def set_#{attribute}(value)
-
6
self.well_attribute.update_attributes!(:#{attribute} => value.to_f)
-
end
-
END_OF_METHOD_DEFINITION
-
end
-
end
-
-
1
def generate_name(_)
-
# Do nothing
-
end
-
-
1
def external_identifier
-
display_name
-
end
-
-
#hotfix
-
1
def well_attribute_with_creation
-
self.well_attribute_without_creation || self.build_well_attribute
-
end
-
1
alias_method_chain(:well_attribute, :creation)
-
-
1
delegate_to_well_attribute(:pico_pass)
-
1
delegate_to_well_attribute(:sequenom_count)
-
1
delegate_to_well_attribute(:gel_pass)
-
1
delegate_to_well_attribute(:study_id)
-
1
delegate_to_well_attribute(:gender)
-
-
1
delegate_to_well_attribute(:concentration)
-
1
alias_method(:get_pico_result, :get_concentration)
-
1
writer_for_well_attribute_as_float(:concentration)
-
-
1
delegate_to_well_attribute(:molarity)
-
1
writer_for_well_attribute_as_float(:molarity)
-
-
1
delegate_to_well_attribute(:current_volume)
-
1
alias_method(:get_volume, :get_current_volume)
-
1
writer_for_well_attribute_as_float(:current_volume)
-
-
1
delegate_to_well_attribute(:buffer_volume, :default => 0.0)
-
1
writer_for_well_attribute_as_float(:buffer_volume)
-
-
1
delegate_to_well_attribute(:requested_volume)
-
1
writer_for_well_attribute_as_float(:requested_volume)
-
-
1
delegate_to_well_attribute(:picked_volume)
-
1
writer_for_well_attribute_as_float(:picked_volume)
-
-
1
delegate_to_well_attribute(:gender_markers)
-
-
1
def update_gender_markers!(gender_markers, resource)
-
if self.well_attribute.gender_markers == gender_markers
-
gender_marker_event = self.events.find_by_family('update_gender_markers', :order => 'id desc')
-
if gender_marker_event.blank?
-
self.events.update_gender_markers!(resource)
-
elsif resource == 'SNP' && gender_marker_event.content != resource
-
self.events.update_gender_markers!(resource)
-
end
-
else
-
self.events.update_gender_markers!(resource)
-
end
-
-
self.well_attribute.update_attributes!(:gender_markers => gender_markers)
-
end
-
-
1
def update_sequenom_count!(sequenom_count, resource)
-
unless self.well_attribute.sequenom_count == sequenom_count
-
self.events.update_sequenom_count!(resource)
-
end
-
self.well_attribute.update_attributes!(:sequenom_count => sequenom_count)
-
-
end
-
-
# The sequenom pass value is either the string 'Unknown' or it is the combination of gender marker values.
-
1
def get_sequenom_pass
-
markers = self.well_attribute.gender_markers
-
markers.is_a?(Array) ? markers.join : markers
-
end
-
-
1
def map_description
-
return read_attribute("map_description") if read_attribute("map_description").present?
-
return nil if map.nil?
-
return nil unless map.description.is_a?(String)
-
-
map.description
-
end
-
-
1
def valid_well_on_plate
-
return false unless self.is_a?(Well)
-
well_plate = plate
-
return false unless well_plate.is_a?(Plate)
-
return false if well_plate.barcode.blank?
-
return false if map_id.nil?
-
return false unless map.description.is_a?(String)
-
-
true
-
end
-
-
1
def create_child_sample_tube
-
Tube::Purpose.standard_sample_tube.create!(:map => self.map, :aliquots => aliquots.map(&:dup)).tap do |sample_tube|
-
AssetLink.create_edge(self, sample_tube)
-
end
-
end
-
-
1
def qc_data
-
{:pico => self.get_pico_pass,
-
:gel => self.get_gel_pass,
-
:sequenom => self.get_sequenom_pass,
-
:concentration => self.get_concentration }
-
end
-
-
1
def buffer_required?
-
get_buffer_volume > 0.0
-
end
-
1
private :buffer_required?
-
-
1
def find_child_plate
-
self.children.reverse_each do |child_asset|
-
return child_asset if child_asset.is_a?(Well)
-
end
-
nil
-
end
-
-
1
validate(:on => :save) do |record|
-
record.errors.add(:name, "cannot be specified for a well") unless record.name.blank?
-
end
-
-
1
def display_name
-
plate_name = self.plate.present? ? self.plate.sanger_human_barcode : '(not on a plate)'
-
plate_name ||= plate.display_name # In the even the plate is barcodeless (ie strip tubes) use its name
-
"#{plate_name}:#{map ? map.description : ''}"
-
end
-
-
1
def details
-
return 'Not yet picked' if plate.nil?
-
plate.purpose.try(:name)||'Unknown plate purpose'
-
end
-
-
1
def can_be_created?
-
plate.stock_plate?
-
end
-
-
1
def latest_stock_metric(product)
-
raise StandardError, 'Too many stock wells to report metrics' if stock_wells.count > 1
-
# If we don't have any stock wells, use ourself. If it is a stock well, we'll find our
-
# qc metric. If its not a stock well, then a metric won't be present anyway
-
stock_well = stock_wells.first || self
-
stock_well.qc_metrics.for_product(product).most_recent_first.first
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014 Genome Research Ltd.
-
1
require 'aasm'
-
-
1
class WellAttribute < ActiveRecord::Base
-
1
include AASM
-
-
1
belongs_to :well, :inverse_of => :well_attribute
-
-
1
serialize :gender_markers
-
1
def gender_markers_string
-
gender_markers.try(:to_s)
-
end
-
-
1
aasm_column :pico_pass
-
-
1
aasm_initial_state :ungraded
-
-
1
aasm_state :ungraded
-
# These states are originally used in SNP
-
1
aasm_state :Pass
-
1
aasm_state :Repeat
-
1
aasm_state :Fail
-
-
# TODO Remvoe 'Too Low To Normalise' from the pico_pass column
-
# The state of 'Too Low To Normalise' exists in the database (from SNP?)
-
# but it doesn't look like AASM can handle spaces in state names.
-
# assm_state :'Too Low To Normalise'
-
-
# Since Pass and Fail are used as pico_state values we're forced
-
# to use a different transition name.
-
1
def pico_pass
-
case self[:pico_pass]
-
when 'Too Low To Normalise' then "Fail"
-
when nil, '' then 'ungraded'
-
else self[:pico_pass]
-
end
-
end
-
-
1
def measured_volume=(volume)
-
self.initial_volume = volume
-
super
-
end
-
-
1
def initial_volume=(volume)
-
super if initial_volume.nil?
-
end
-
-
1
def quantity_in_nano_grams
-
return nil if measured_volume.nil? || concentration.nil?
-
return nil if measured_volume < 0 || concentration < 0
-
-
(measured_volume * concentration).to_i
-
end
-
-
1
def quantity_in_micro_grams
-
return nil if measured_volume.nil? || concentration.nil?
-
return nil if measured_volume < 0 || concentration < 0
-
(measured_volume * concentration)/1000
-
end
-
-
1
aasm_event :pass_pico_test do
-
1
transitions :to => :Pass, :from => [:ungraded, :Repeat, :Fail, :Pass]
-
end
-
-
1
aasm_event :fail_pico_test do
-
1
transitions :to => :Fail, :from => [:Repeat, :Fail, :Pass]
-
1
transitions :to => :Repeat, :from => [:ungraded]
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
class WorkingDilutionPlate < DilutionPlate
-
1
self.prefix = "WD"
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
1
class WorkingDilutionPlatePurpose < PlatePurpose
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2013,2014,2015 Genome Research Ltd.
-
class AmqpObserver < ActiveRecord::Observer
-
# Observe not only the records but their metadata too, otherwise we may miss changes.
-
observe(
-
:order, :submission, :request, :plate_purpose,
-
:study, :study_sample, :sample, :aliquot, :tag,
-
:project,
-
:asset, :asset_link, :well_attribute,
-
Metadata::Base,
-
:batch, :batch_request,
-
:role, Role::UserRole,
-
:reference_genome,
-
:messenger,
-
:broadcast_event
-
)
-
-
# Ensure we capture records being saved as well as deleted.
-
#
-
# NOTE: Oddly you can't alias_method the after_destroy, it has to be physically defined!
-
class_eval(%Q{def after_save(record) ; self << record ; true ; end})
-
class_eval(%Q{def after_destroy(record) ; record.class.render_class.associations.each {|a,_| record.send(a) } ; self << record ; true ; end})
-
-
# To prevent ActiveRecord::Observer doing something insane when we test this, we pull
-
# out the implementation in a module (which can be tested) and leave the rest behind.
-
module Implementation
-
def self.included(base)
-
1
base.class_eval do
-
1
attr_reader :exchange
-
1
private :exchange
-
end
-
end
-
-
# A transaction is (potentially) a bulk send of messages and hence we can create a buffer that
-
# will be written to during changes. But transactions can be nested, which means that only the
-
# very outer one should do any publishing.
-
#
-
#--
-
# What follows looks complicated but is specialised to deal with a 'return' being called from
-
# within the block. In that case the 'return' causes the method to return and *not* to execute
-
# any code after 'yield' in the code below. Therefore you have the situation where the database
-
# transaction has been committed but the AMQP broadcast has not happened. But the 'ensure' block
-
# is always called, so we assume the transaction to be good (i.e. commit) unless an exception is
-
# raised (when it is marked as bad), and broadcast our buffer iff the transaction is good and
-
# there's stuff to broadcast.
-
#++
-
def transaction(&block)
-
Thread.current[:buffer] ||= (current_buffer = MostRecentBuffer.new(self))
-
transaction_good = true
-
yield
-
rescue => exception
-
transaction_good = false
-
raise
-
ensure
-
activate_exchange do
-
current_buffer.map(&method(:publish))
-
end if transaction_good and not current_buffer.blank?
-
Thread.current[:buffer] = nil unless current_buffer.nil?
-
end
-
-
def <<(record)
-
buffer << record
-
self # Ensure we can chain these if necessary!
-
end
-
-
# Converts metadata entries to their owner records, if necessary
-
def determine_record_to_broadcast(record, &block)
-
case
-
when record.nil? then nil # Do nothing if we have no record.
-
# This occurs with roles with no authorizable, but may also happen in cases where we have
-
# orphaned records.
-
when record.is_a?(WellAttribute) then yield(record.well, nil)
-
when record.is_a?(Metadata::Base) then yield(record.owner, nil)
-
when record.is_a?(Role) then determine_record_to_broadcast(record.authorizable, &block)
-
when record.is_a?(Role::UserRole) then determine_record_to_broadcast(record.role, &block)
-
else yield(record, record)
-
end
-
end
-
private :determine_record_to_broadcast
-
-
# A simple buffer class that will only retain the most recent version of any object pushed
-
# into it. Assumes that equality is what you want for checking for things, which works fine
-
# with ActiveRecord.
-
class MostRecentBuffer
-
def initialize(observer)
-
@observer, @updated, @deleted = observer, Set.new, Set.new
-
end
-
-
delegate :determine_record_to_broadcast, :to => :@observer
-
-
def map(&block)
-
@updated.group_by(&:first).each do |model, pairs|
-
# Regardless of what the scoping says, we're going by ID so we always want to do what
-
# the standard model does. If we need eager loading we'll add it.
-
model.send(:with_exclusive_scope) do
-
model = model.including_associations_for_json if model.respond_to?(:including_associations_for_json)
-
pairs.map(&:last).in_groups_of(configatron.amqp.burst_size).each { |group| model.find(group.compact).map(&block) }
-
end
-
end
-
@deleted.map(&block)
-
end
-
-
def <<(record)
-
self.tap do
-
determine_record_to_broadcast(record) do |record_to_broadcast, record_for_deletion|
-
pair = [ record_to_broadcast.class, record_to_broadcast.id ]
-
if record.destroyed?
-
@updated.delete(pair)
-
@deleted << record_for_deletion if record_for_deletion.present?
-
else
-
@updated << pair
-
end
-
end
-
end
-
end
-
-
def blank?
-
@updated.empty? and @deleted.empty?
-
end
-
end
-
-
# A very simply proxy around the observer such that it will ensure the exchange is activated.
-
# This should mean that `AmqpObserver.instance << record` will work, even without the surrounding
-
# transaction.
-
class Proxy
-
def initialize(observer)
-
@observer = observer
-
end
-
1
-
delegate :activate_exchange, :publish, :determine_record_to_broadcast, :to => :@observer
-
1
private :activate_exchange, :publish
-
-
1
def <<(record)
-
activate_exchange do
-
determine_record_to_broadcast(record) do |record_to_broadcast, record_for_deletion|
-
Rails.logger.warn { "AmqpObserver called outside transaction: #{caller.join("\n")}" }
-
-
if record.destroyed?
-
publish(record_for_deletion) if record_for_deletion.present?
-
else
-
publish(record_to_broadcast)
-
end
-
end
-
end
-
end
-
end
-
-
1
def publish(record)
-
exchange.publish(
-
MultiJson.dump(record),
-
:key => record.routing_key||"#{Rails.env}.saved.#{record.class.name.underscore}.#{record.id}",
-
:persistent => configatron.amqp.persistent
-
)
-
end
-
1
private :publish
-
-
# The buffer that should be written to is either the one created within the transaction, or it is a
-
# wrapper around ourselves.
-
1
def buffer
-
Thread.current[:buffer] || Proxy.new(self)
-
end
-
1
private :buffer
-
-
# The combination of Bunny & Mongrel means that, unless you start & stop the Bunny connection,
-
# the Mongrel process will start killing threads because of too many open files. This method,
-
# therefore, enables transactional support for connecting to the exchange.
-
1
def activate_exchange(&block)
-
return yield unless @exchange.nil?
-
-
client = Bunny.new(configatron.amqp.url, :spec => '09', :frame_max => configatron.amqp.fetch(:maximum_frame,0))
-
begin
-
client.start
-
@exchange = client.exchange('psd.sequencescape', :passive => true)
-
yield
-
ensure
-
@exchange = nil
-
client.stop
-
end
-
rescue Qrack::ConnectionTimeout, StandardError => exception
-
Rails.logger.error { "Unable to broadcast: #{exception.message}\n#{exception.backtrace.join("\n")}" }
-
end
-
1
private :activate_exchange
-
end
-
1
include Implementation
-
end
-
-
class ActiveRecord::Base
-
class << self
-
def transaction_with_amqp(&block)
-
transaction_without_amqp { AmqpObserver.instance.transaction(&block) }
-
end
-
alias_method_chain(:transaction, :amqp)
-
end
-
def routing_key;nil;end
-
1
end if ActiveRecord::Base.observers.include?(:amqp_observer)
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2015 Genome Research Ltd.
-
# A cache sweeper that clears out the batch XML that we're caching to improve the performance
-
# of NPG.
-
1
class BatchCacheSweeper < ActiveRecord::Observer
-
1
include XmlCacheHelper
-
-
# All of the following models have some affect on the batch XML that we're caching
-
1
observe Batch, BatchRequest, Request, LibraryTube, MultiplexedLibraryTube, Lane, Aliquot, Tag
-
-
#Â The controller we're caching
-
1
set_caching_for_controller 'batches'
-
1
set_caching_for_model 'batches'
-
-
# This is an ordered hash, mapping from a model name to the SQL JOIN that needs to be added.
-
# It's used to automatically generate the correct query to find the associated batch for a record.
-
1
JOINS = ActiveSupport::OrderedHash.new.tap do |joins|
-
1
joins['batch_requests'] = "INNER JOIN batch_requests ON batch_requests.batch_id=batches.id"
-
1
joins['requests'] = "INNER JOIN requests ON requests.id=batch_requests.request_id"
-
1
joins['aliquots'] = "INNER JOIN aliquots ON (aliquots.receptacle_id=requests.asset_id OR aliquots.receptacle_id=requests.target_asset_id)"
-
end
-
-
1
def through(record, &block)
-
model, conditions = case
-
when record.is_a?(BatchRequest) then [ 'batch_requests', query_conditions_for(record) ]
-
when record.is_a?(Request) then [ 'batch_requests', "batch_requests.request_id=#{record.id}" ]
-
when record.is_a?(Asset) then [ 'requests', "(requests.asset_id=#{record.id} OR requests.target_asset_id=#{record.id})" ]
-
when record.is_a?(Aliquot) then [ 'aliquots', query_conditions_for(record) ]
-
when record.is_a?(Tag) then [ 'aliquots', "aliquots.tag_id=#{record.id}" ]
-
end
-
yield(JOINS.values.slice(0, JOINS.keys.index(model)+1), conditions)
-
end
-
1
private :through
-
-
1
def query_details_for(record, &block)
-
return yield([], query_conditions_for(record)) if record.is_a?(Batch)
-
through(record, &block)
-
end
-
1
private :query_details_for
-
-
1
def handle(record)
-
debug { "Rebroadcasting message for #{record.class.name}(#{record.id})" }
-
# record.messengers.each(&:touch)
-
messengers_for(record).each(&:resend)
-
super
-
end
-
1
private :handle
-
-
1
def messengers_for(record)
-
Messenger.find(:all,:conditions=>{:target_type=>'Batch',:target_id=>ids_for(record)})
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
1
class RequestObserver < ActiveRecord::Observer
-
-
1
def after_create(request)
-
request.request_events.create!(
-
:event_name => 'created',
-
:to_state => request.state,
-
:current_from => DateTime.now
-
)
-
end
-
-
1
def before_save(request)
-
return if request.new_record? || !request.changed.include?('state')
-
from_state = request.changes['state'].first
-
time = DateTime.now
-
request.current_request_event.expire!(time) unless request.current_request_event.nil?
-
request.request_events.create!(
-
:event_name => 'state_changed',
-
:from_state => from_state,
-
:to_state => request.state,
-
:current_from => time
-
)
-
end
-
-
1
def before_destroy(request)
-
time = DateTime.now
-
request.current_request_event.expire!(time) unless request.current_request_event.nil?
-
request.request_events.create!(
-
:event_name => 'destroyed',
-
:from_state => request.state,
-
:to_state => request.state,
-
:current_from => time,
-
:current_to => time
-
)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class SampleCacheSweeper < ActiveRecord::Observer
-
1
include XmlCacheHelper
-
1
observe Sample, Sample::Metadata, StudySample, Study, ReferenceGenome, Aliquot, Aliquot::Receptacle
-
1
set_caching_for_controller 'samples'
-
1
set_caching_for_model 'samples'
-
-
1
THROUGH_JOINS = {
-
'study' => "INNER JOIN study_samples ON study_samples.sample_id=samples.id",
-
'receptacle' => "INNER JOIN aliquots ON aliquots.sample_id=samples.id"
-
}
-
-
# We shorten the query conditions for studies and receptacles because we do not need to perform
-
# a JOIN against their table, considering we go through a JOIN table anyway.
-
1
def through(record, &block)
-
model, conditions = case
-
when record.is_a?(StudySample) then [ 'study', query_conditions_for(record) ]
-
when record.is_a?(Study) then [ 'study', "study_samples.study_id=#{record.id}" ]
-
when record.is_a?(Aliquot) then [ 'receptacle', query_conditions_for(record) ]
-
when record.is_a?(Aliquot::Receptacle) then [ 'receptacle', "aliquots.receptacle_id=#{record.id}" ]
-
end
-
yield(Array(THROUGH_JOINS[model]), conditions)
-
end
-
1
private :through
-
-
1
def query_details_for(record, &block)
-
case
-
when record.is_a?(Sample) then yield([], query_conditions_for(record))
-
when record.is_a?(Sample::Metadata) then metadata(record, &block)
-
when record.is_a?(ReferenceGenome) then metadata_association(:reference_genome, record, &block)
-
else through(record, &block)
-
end
-
end
-
1
private :query_details_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
1
class StudyCacheSweeper < ActiveRecord::Observer
-
1
include XmlCacheHelper
-
1
observe Study, Study::Metadata, StudyType, DataReleaseStudyType, ReferenceGenome, FacultySponsor
-
1
set_caching_for_controller 'studies'
-
1
set_caching_for_model 'studies'
-
-
# Determines the JOINs necessary to identify any studies that are affected by the record change. Note
-
# that once you get a Study::Metadata record you always need to include that table, but the others do
-
# not go through each other (like the BatchCacheSweeper does).
-
1
def query_details_for(record, &block)
-
case
-
when record.is_a?(Study) then yield([], query_conditions_for(record))
-
when record.is_a?(Study::Metadata) then metadata(record, &block)
-
when record.is_a?(StudyType) then metadata_association(:study_type, record, &block)
-
when record.is_a?(DataReleaseStudyType) then metadata_association(:data_release_study_type, record, &block)
-
when record.is_a?(ReferenceGenome) then metadata_association(:reference_genome, record, &block)
-
when record.is_a?(FacultySponsor) then metadata_association(:faculty_sponsor, record, &block)
-
end
-
end
-
1
private :query_details_for
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
module XmlCacheHelper
-
# Include this module into the controller and use cache_xml_response to cache the XML response
-
# appropriately for a cache sweeper that includes the XmlCacheHelper module.
-
module ControllerHelper
-
def cache_xml_response(record)
-
render :layout => false
-
cache_page(
-
response.body,
-
url_for(
-
:controller => self.class.controller_name,
-
:action => self.action_name,
-
:id => record.id,
-
:format => :xml,
-
:only_path => true
-
)
-
)
-
end
-
private :cache_xml_response
-
end
-
-
def self.included(base)
-
base.class_eval do
-
extend ClassMethods
-
-
delegate :debug, :to => 'Rails.logger'
-
-
# NOTE: Getting sweepers to work without a controller is not simple!
-
#
-
# All the documentation suggests that you should be able to simply define the observer methods
-
# and then call expire_page with the url_for details. However, this only works if the sweeper
-
# has been paired with a controller, so that the controller is inserted into the sweeper.
-
#
-
# This is the best way of doing this, hence we simply implement an observer here and then
-
# include & delegate everything we need to interact with the cache.
-
include Rails.application.routes.url_helpers
-
1
-
delegate :expire_page, :perform_caching, :to => 'ActionController::Base'
-
1
alias_method(:perform_caching?, :perform_caching)
-
1
private :expire_page, :perform_caching, :perform_caching?
-
1
private :url_for
-
end
-
end
-
-
# After saving do cleanup of the cache.
-
def after_save(record)
-
handle(record)
-
end
-
-
# Before destroying do clean up of the cache, as after will be too late!
-
def before_destroy(record)
-
handle(record)
-
end
-
-
def handle(record)
-
return unless perform_caching?
-
debug { "Sweeping #{caching_for_controller} cache from #{record.class.name}(#{record.id})" }
-
ids_for(record).compact.uniq.map(&method(:clear_cache))
-
debug { "Cache sweeping of #{caching_for_controller} complete for #{record.class.name}(#{record.id})" }
-
end
-
private :handle
-
-
def clear_cache(id)
-
expire_page(url_for(
-
:controller => caching_for_controller, :action => 'show', :id => id, :format => :xml,
-
:only_path => true, :skip_relative_url_root => true
-
))
-
rescue Errno::ENOENT => exception
-
Rails.logger.warn { "Cannot clear cached XML file as it does not exist (#{exception.message})" }
-
end
-
private :clear_cache
-
-
# Finds all of the batches that the specified record relates to
-
def ids_for(record)
-
query_details_for(record) do |joins, conditions|
-
query = %Q{
-
SELECT DISTINCT #{caching_for_model}.id AS id
-
FROM #{caching_for_model} #{Array(joins).uniq.join(' ')}
-
WHERE #{Array(conditions).uniq.join(' AND ')}
-
}
-
ActiveRecord::Base.connection.select_all(query).map { |result| result['id'] }
-
end
-
end
-
private :ids_for
-
-
def query_conditions_for(record)
-
"#{record.class.table_name}.id=#{record.id}"
-
end
-
private :query_conditions_for
-
-
def metadata(record, &block)
-
metadata = "#{caching_for_model.to_s.singularize}_metadata"
-
yield(
-
"INNER JOIN #{metadata} ON #{metadata}.#{caching_for_model.to_s.singularize}_id=#{caching_for_model.to_s.pluralize}.id",
-
"#{metadata}.id=#{record.id}")
-
end
-
private :metadata
-
-
def metadata_association(type, record, &block)
-
metadata = "#{caching_for_model.to_s.singularize}_metadata"
-
yield(
-
"INNER JOIN #{metadata} ON #{metadata}.#{caching_for_model.to_s.singularize}_id=#{caching_for_model.to_s.pluralize}.id",
-
"#{metadata}.#{type.to_s.singularize}_id=#{record.id}")
-
end
-
private :metadata_association
-
-
module ClassMethods
-
def self.extended(base)
-
base.class_eval do
-
delegate :caching_for_controller, :caching_for_model, :to => 'self.class'
-
end
-
end
-
-
def set_caching_for_controller(name)
-
1
@caching_for_controller = name
-
end
-
-
def caching_for_controller
-
@caching_for_controller
-
end
-
-
def set_caching_for_model(name)
-
1
@caching_for_model = name
-
end
-
-
def caching_for_model
-
@caching_for_model
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2014 Genome Research Ltd.
-
1
require 'carrierwave'
-
-
1
module CarrierWave
-
1
module Storage
-
# Database storage - puts the file contents in a database table
-
1
class DirectDatabase < Abstract
-
# Store: Takes a file object, passes it to a file wrapper class which handles storage in the DB
-
1
def store!(file)
-
f = CarrierWave::Storage::DirectDatabase::File.new(uploader, self, uploader.store_path)
-
f.store(file.read)
-
f
-
end
-
-
# Retrieve: Returns a file wrapper which accesses the database via the passed model
-
1
def retrieve!(identifier)
-
CarrierWave::Storage::DirectDatabase::File.new(uploader, self, uploader.store_path(identifier))
-
end
-
-
1
class File
-
1
def initialize(uploader, base, path)
-
@uploader = uploader
-
@path = path
-
@base = base
-
end
-
-
# Returns the current path of the file
-
1
def path
-
@path
-
end
-
-
1
def size
-
current_data.size
-
end
-
-
# Reads the contents of the file
-
1
def read
-
current_data
-
end
-
-
# Remove the file
-
1
def delete
-
puts "Deleting file from database"
-
destroy_file
-
end
-
-
# Would returns the url
-
1
def url
-
raise NotImplementedError, "Files are stored in the database, so are not available directly through a URL"
-
end
-
-
# Stores the file in the DbFiles model - split across many rows if size > 200KB
-
1
def store(file)
-
each_slice(file) do |start, finish|
-
@uploader.model.db_files.create!(:data => file.slice(start, finish))
-
end
-
-
# Old code from attachment_fu: doesn't seem to be needed
-
# # self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
-
end
-
-
# Error handling should help if uploader was mounted to a model with no content_type
-
1
def content_type
-
@uploader.model.content_type if @uploader.model.respond_to? :content_type
-
end
-
-
1
def content_type=(type)
-
if @uploader.model.respond_to? :content_type
-
@uploader.model.content_type=type unless type.nil?
-
end
-
end
-
-
1
private
-
# Gets the current data from the database
-
1
def current_data
-
@uploader.model.db_files.map(&:data).join
-
end
-
-
# Destroys the file. Called in the after_destroy callback
-
1
def destroy_file
-
@uploader.model.db_files.each do |db_file|
-
db_file.delete
-
end
-
end
-
-
# Yields the partitions for the file with the max_part_size boundary
-
1
def each_slice(data)
-
max_part_size = 200.kilobytes
-
beginning =0;
-
left = data.size
-
while left>0
-
part_size = [left, max_part_size].min
-
yield beginning, part_size
-
beginning += part_size
-
left -= part_size
-
end
-
end
-
end
-
end # Database
-
end # Storage
-
end # CarrierWave
-
1
class PolymorphicUploader < CarrierWave::Uploader::Base
-
-
1
def initialize(*args, &block)
-
super
-
end
-
-
1
def exists?
-
@column.blank?
-
end
-
-
1
storage CarrierWave::Storage::DirectDatabase
-
-
# This is where files are stored on upload. We are using callbacks to empty it after upload
-
1
def self.cache_dir
-
"#{Rails.root}/tmp/uploads"
-
end
-
-
1
def cache_dir
-
self.class.cache_dir
-
end
-
-
-
1
before :store, :remember_cache_id
-
1
after :store, :delete_tmp_dir
-
-
# store! nils the cache_id after it finishes so we need to remember it for deletion
-
1
def remember_cache_id(new_file)
-
@cache_id_was = cache_id
-
end
-
-
1
def delete_tmp_dir(new_file)
-
# make sure we don't delete other things accidentally by checking the name pattern
-
if @cache_id_was.present? && @cache_id_was =~ /\A[\d]{8}\-[\d]{4}\-[\d]+\-[\d]{4}\z/
-
FileUtils.rm_rf(File.join(cache_dir, @cache_id_was))
-
end
-
end
-
-
-
end
-
-
-
1
<%#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.%>
-
1
<% add(:tab, I18n.t('navigation.tabs.studies') => studies_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.projects') => projects_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.admin') => admin_path) if logged_in? && current_user.is_administrator? -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.pipelines') => pipelines_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.batches') => batches_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.reception') => receptions_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.labwhere_reception') => labwhere_receptions_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.labview') => lab_view_path) -%>
-
1
<% add(:tab, I18n.t('navigation.tabs.labsearch') => new_lab_search_path) -%>
-
1
-
<!DOCTYPE html>
-
<html lang="en">
-
-
<head>
-
<meta charset="UTF-8" />
-
<title>
-
1
Sequencescape : <%= controller.controller_name.capitalize %> - <%= @page_name || controller.action_name %>
-
1
</title>
-
<meta name="viewport" content="width=device-width, initial-scale=1">
-
1
<%= stylesheet_link_tag "tabview", "application", :media => "all", :cache => false %>
-
1
-
-
<%#= stylesheet_link_tag("formtastic") + stylesheet_link_tag("formtastic_changes") %>
-
1
<%= javascript_include_tag "application" %>
-
1
-
1
<%= csrf_meta_tags %>
-
1
-
<link rel="shortcut icon" href="/favicon.ico" type="image/vnd.microsoft.icon" />
-
<link rel="icon" href="/favicon.ico" type="image/vnd.microsoft.icon" />
-
</head>
-
-
1
<body class="<%= Rails.env %> <%= DeploymentEnvironment.role %> yui-skin-sam">
-
<div id="doc3" class="container-fluid yui-t4">
-
-
1
<header><%= render :partial => 'shared/header' %></header>
-
-
<section class="clearfix main-row">
-
<section class="col-md-10">
-
1
<%= render_flashes %>
-
1
<div id="yield">
-
1
<%= yield %>
-
1
</div>
-
</section>
-
1
<div id="menu" class="col-md-2"><%= render :partial => 'shared/sidebar' %></div>
-
</section>
-
-
<footer>
-
<div class="well">
-
1
<%= link_to "Feedback", "mailto:#{configatron.admin_email}" %> · <%= Deployed::VERSION_STRING %> · A tool from <%= link_to configatron.fetch(:team_name,'LIMS and Informatics'), configatron.fetch(:team_url,'http://www.sanger.ac.uk/science/groups/production-software-development') %>
-
1
</div>
-
</footer>
-
-
</div>
-
</body>
-
</html>
-
2
<%= bs_column do %>
-
1
<h2>Samples</h2>
-
1
<%= pagination samples %>
-
1
-
1
<% samples.each do |sample| %>
-
5
<%= link_to "#{sample.sanger_sample_id} (#{sample.name})", sample_path(sample) %><br />
-
<% end %>
-
1
<%= pagination samples %>
-
1
-
1
<% if samples.size == 0 %>
-
<div class="help">
-
Samples may take a few minutes to be processed.
-
</div>
-
<% end %>
-
1
<% end %>
-
1
<%#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.%>
-
1
<% add :menu, "SampleDB Home" => '/sdb/' %>
-
1
<% add :menu, " " => '' -%>
-
1
<% add :menu, "Create manifest for plates" => new_sample_manifest_path({:type => "plate"}) %>
-
1
<% add :menu, "Create manifest for 1D tubes" => new_sample_manifest_path({:type => "1dtube"}) %>
-
1
<% add :menu, "Create manifest for multiplexed libraries" => new_sample_manifest_path({:type => "multiplexed_library"}) %>
-
1
<% add :menu, "View all manifests" => sample_manifests_path %>
-
1
<% add :menu, " " => '' -%>
-
1
<% add :menu, "Create supplier" => new_supplier_path %>
-
1
<% add :menu, "View all suppliers" => suppliers_path %>
-
1
<% add :menu, " " => '' -%>
-
1
<% add :menu, "Create study" => new_study_path() %>
-
1
<% add :menu, "Create project" => new_project_path() %>
-
1
<%#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.%>
-
1
<h2>Upload a sample manifest</h2>
-
-
2
<%= form_for :sample_manifest, :url => upload_sample_manifests_url, :html =>{ :multipart => true } do |form| -%>
-
1
<label style='display: none;' for="sample_manifest_uploaded">File to upload</label>
-
1
<%= form.file_field :uploaded %><br/><br />
-
<%= form.check_box :override %><label for="sample_manifest_override">Override previously uploaded samples</label>
-
1
<br/><br />
-
<%= form.submit 'Upload manifest' %>
-
1
<% end %>
-
1
<%#This file is part of SEQUENCESCAPE; it is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.%>
-
1
<%= render :partial => "side_links" %>
-
1
<% add :menu, "Download Blank Manifest" => export_sample_manifest_path(@sample_manifest) %>
-
1
-
1
<h2>Manifest "<%= @sample_manifest.name %>" for study <%= @sample_manifest.study.name %></h2>
-
1
<h3><%= link_to "Download Blank Manifest", export_sample_manifest_path(@sample_manifest) %></h3>
-
-
1
<% if @sample_manifest.user %>
-
<p>Created by <%= link_to @sample_manifest.user.login, profile_path(@sample_manifest.user) %></p>
-
<% end %>
-
1
-
1
<%= render :partial => "upload" %>
-
1
-
1
<% if @sample_manifest.last_errors %>
-
<h2>Errors</h2>
-
<div class="help">
-
Since there are errors no samples have been updated. Please fix them and upload again.
-
</div>
-
<ul>
-
-
<% @sample_manifest.last_errors.each do |error| %>
-
<li><%= error %></li>
-
<% end %>
-
</ul>
-
<% end %>
-
1
-
1
<% if @sample_manifest.barcodes %>
-
-
<%= bs_column do %>
-
<h2>Barcodes</h2>
-
<ul>
-
<% @sample_manifest.barcodes.each do |barcode| %>
-
<li><%= barcode %></li>
-
<% end %>
-
</ul>
-
<% end %>
-
<% end %>
-
1
-
1
<%= render partial: "samples", locals: { samples: @samples} %>
-
1
-
<div class="header small">
-
<ul class="list-inline">
-
1
<li><span id="institute"><%= link_to I18n.t('application.institute'), I18n.t('application.institute_link') %></span></li>
-
1
<% if logged_in? -%>
-
1
<li>
-
1
<%= I18n.t('global_links.welcome') %> <%= link_to current_user.name, profile_path(current_user) %> ·
-
</li>
-
<li>
-
1
<%= I18n.t('global_links.workflow') %>
-
1
<% unless current_user.workflow.nil? -%>
-
1
<%= link_to current_user.workflow_name, edit_profile_path(current_user) %> ·
-
<% else -%>
-
<strong><%= link_to I18n.t('global_links.workflow_not_set'), edit_profile_path(current_user) %></strong> ·
-
<% end -%>
-
1
</li>
-
<li>
-
1
<%= link_to I18n.t('global_links.logout'), logout_path -%>
-
</li>
-
1
<% end -%>
-
</ul>
-
</div>
-
-
<nav class="navbar navbar-default">
-
<div class="container-fluid">
-
-
<!-- navbar-header remains visible even at low resolutions. It groups the logo with a toggle button which appears if the menu collapses -->
-
<div class="navbar-header">
-
<div id="logo_container" class="navbar-brand">
-
1
<div id="sequencescape_logo">
-
1
<%= link_to image_tag("sequencescape.gif"), '/' %>
-
1
</div>
-
<% if Rails.env == 'production' %>
-
<div id="app_name"><%= link_to I18n.t('application.name').upcase, '/' %></div>
-
1
<% else %>
-
<div id="app_name"><%= link_to Rails.env.humanize, '/' %></div>
-
1
<% end %>
-
</div>
-
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
-
<span class="sr-only">Toggle navigation</span>
-
<!-- These spans for the bars of the hamburger (menu) navigation -->
-
<span class="icon-bar"></span>
-
<span class="icon-bar"></span>
-
<span class="icon-bar"></span>
-
</button>
-
</div>
-
<!-- The main body of the navbar. Collapses at low resolutions -->
-
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
-
1
<ul class="nav navbar-nav">
-
1
<%= render :partial => 'shared/tab', :collection => @tabs.items %>
-
2
</ul>
-
1
<%= form_tag searches_path, :method => :get, :class => "navbar-form navbar-right", :role => "search" do %>
-
1
<div class="form-group">
-
1
<%= search_field_tag :q, "", :size => 20, :type => "search", :placeholder => "name, id, barcode", :id => 'layout_search', :class=>"form-control" %>
-
1
<%= select_tag :type, options_for_select(search_options,""), :id => 'layout_search_options', :class=>"form-control" %>
-
</div>
-
1
<% end %>
-
</div><!-- /.navbar-collapse -->
-
-
</div>
-
</nav>
-
1
-
<%- unless custom_text("app_info_box", 1).blank? -%>
-
<div id="app-info-box" class="well well-sm">
-
<span><%= custom_text("app_info_box", 1) %></span>
-
</div>
-
1
<%- end -%>
-
1
<% if @manager_menu -%>
-
1
<% if logged_in? && current_user.manager_or_administrator? -%>
-
<ul id="manager_menu" class="nav nav-pills nav-stacked">
-
<%= render :partial => "/item", :collection => @manager_menu.items %>
-
</ul>
-
<% end -%>
-
<% end -%>
-
1
-
1
<% if @menu -%>
-
1
<ul id="base-Menu" class="nav nav-pills nav-stacked">
-
1
<%= render :partial => "/item", :collection => @menu.items %>
-
1
</ul>
-
<% end -%>
-
1
-
1
<% if @admin_menu -%>
-
1
<% if logged_in? && current_user.administrator? -%>
-
<ul id="admin_menu" class="nav nav-pills nav-stacked">
-
<%= render :partial => "/item", :collection => @admin_menu.items %>
-
</ul>
-
<% end -%>
-
<% end -%>
-
1
-
1
<% if @back_menu -%>
-
1
<ul id="back_menu" class="nav nav-pills nav-stacked">
-
<%= render :partial => "/item", :collection => @back_menu.items %>
-
</ul>
-
<% end -%>
-
1
-
1
<% if @legend -%>
-
1
<ul id="legend" class="nav nav-pills nav-stacked">
-
<%= render :partial => "/item", :collection => @legend.items %>
-
</ul>
-
<% end -%>
-
1
<% if @about.present? %>
-
<%= panel(:default,title:"About this page") do %>
-
<%= @about %>
-
<% end %>
-
<% end %>
-
1
-
2
<%= panel(:default,title:"Links") do %>
-
1
<%= render :partial => "shared/menu" %>
-
<% end %>
-
1
-
2
<%= panel(:info) do %>
-
1
<%= image_tag "information.png", :size => "16x16" %><span style="vertical-align: middle;">If you need help using Sequencescape please email:</span> <span style="text-align: center"><%= help_email_link %></span>
-
<% end %>
-
8
<li><%= link_to tab.text, tab.link %></li>
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
Request.find_all_by_request_type_id([2,3]).each do |req|
-
if req.asset && req.asset.parent
-
req.asset.parent.requests.each do |lc|
-
if req.item_id != lc.item_id && !lc.target_asset.nil? && lc.target_asset == req.asset
-
puts "#{req.id} #{req.item_id} #{req.asset_id} #{req.state} #{req.study.name} #{lc.id} #{lc.item_id} #{lc.target_asset_id} #{lc.state} #{lc.study.name}"
-
puts " req.item_id = #{lc.item_id}"
-
end
-
end
-
end
-
end
-
1
module ActiveRecord # :nodoc:
-
1
module Acts #:nodoc:
-
1
module Descriptable
-
1
def self.included(base) # :nodoc:
-
1
base.extend(ClassMethods)
-
end
-
-
1
module ClassMethods
-
1
def acts_as_descriptable(style = :serialized)
-
4
include "ActiveRecord::Acts::Descriptable::InstanceMethods::#{style.to_s.classify}".constantize
-
4
extend ActiveRecord::Acts::Descriptable::SingletonMethods
-
end
-
end
-
-
1
module SingletonMethods
-
1
def search(descriptors)
-
conditions = ""
-
for descriptor in descriptors
-
conditions += "descriptors LIKE '%"
-
conditions += descriptor.name + ": \"" + descriptor.value + "\"%' or "
-
end
-
conditions.gsub!(/ or $/, "")
-
logger.info "Searching for: " + conditions
-
self.find(:all, :conditions => conditions)
-
end
-
-
1
def find_descriptors
-
logger.info "Finding all descriptors"
-
response = []
-
self.find(:all).each do |object|
-
object.descriptors.each do |descriptor|
-
response.push descriptor
-
end
-
end
-
response
-
end
-
end
-
-
1
module InstanceMethods
-
1
module Active
-
1
def self.included(base)
-
2
base.class_eval do
-
2
has_many :descriptors, :order => 'sorter', :dependent => :destroy
-
end
-
end
-
-
1
def create_descriptors(params)
-
self.descriptors << params.sort_by { |k,_| k.to_i }.each_with_index.map do |(field_id, value), index|
-
value[:required] = (value[:required] == 'on') ? 1 : 0
-
Descriptor.new(value.merge(:sorter => index+1))
-
end
-
end
-
-
1
def update_descriptors(params)
-
delete_descriptors
-
create_descriptors(params)
-
end
-
-
1
def delete_descriptors
-
self.descriptors.clear
-
end
-
end
-
-
1
module Serialized
-
1
def self.included(base)
-
2
base.class_eval do
-
2
serialize :descriptors
-
2
serialize :descriptor_fields
-
end
-
end
-
-
1
def descriptor_xml(options = {})
-
xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
-
xml.instruct! unless options[:skip_instruct]
-
-
xml.descriptors {
-
self.descriptors.each do |field|
-
xml.descriptor {
-
descriptor.name field.name.to_s
-
descriptor.value field.value
-
}
-
end
-
}
-
end
-
-
1
def descriptors
-
[].tap do |response|
-
each_descriptor do |field, value|
-
response.push(Descriptor.new(:name => field, :value => value))
-
end
-
end
-
end
-
-
1
def each_descriptor(&block)
-
descriptor_hash = read_descriptor_hash
-
read_descriptor_fields.each do |field|
-
next if field.blank?
-
yield(field, descriptor_hash[field])
-
end
-
end
-
-
1
def descriptor_value(key)
-
read_descriptor_hash.fetch(key, '')
-
end
-
-
1
def add_descriptor(descriptor)
-
write_attribute(:descriptors, read_descriptor_hash.merge(descriptor.name => descriptor.value))
-
write_attribute(:descriptor_fields, read_descriptor_fields.push(descriptor.name))
-
end
-
-
1
def read_descriptor_hash
-
read_attribute(:descriptors) || {}
-
end
-
1
private :read_descriptor_hash
-
-
1
def read_descriptor_fields
-
read_attribute(:descriptor_fields) || []
-
end
-
1
private :read_descriptor_fields
-
end
-
end
-
end
-
end
-
end
-
#!/usr/bin/env ruby
-
-
# Copyright (c) 2006 by Zak Mandhro
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
-
# and associated documentation files (the "Software"), to deal in the Software without restriction,
-
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
-
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
-
# subject to the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be included in all copies or substantial
-
# portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
-
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
-
# EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-
# USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#
-
# = Rails Plug-in Package Task
-
#
-
# RailsPluginPackageTask is a rake task designed to automate the publishing of
-
# Ruby on Rails plug-ins. The Rails plug-in installer _RecursiveHTTPFetcher_ makes
-
# certain assumptions about the web servers that does not hold through from
-
# server to server, for example:
-
#
-
# * Server generates an index page with links
-
# * All links to plug-in files are relative links
-
# * Folder links end with a forward slash (used to recurse)
-
#
-
# RubyForge web server is an example of where these assupmtions don't hold
-
# true. As a result, you can not simply copy your files to a web server
-
# and expect Rails HTTP plugin installer to just work.
-
#
-
# This Rake task helps fill the gap by complying to the plug-in scripts assumptions.
-
# Following the Rake package task conventions, it defines the "rails_plugin" task
-
# that recurses through your _package_files_, generates compliant index.html for
-
# each folder (that contains a file), and creates a directory structure that you
-
# can publish as a set for your plugin.
-
#
-
# = Example
-
#
-
# The following example uses the Rake::RailsPluginPackageTask to create
-
# the package. It then uses the Rake::SshDirPublisher to publish the plugin
-
# directory to RubyForge.
-
# #
-
# Rake::RailsPluginPackageTask.new(ProjectInfo[:name], ProjectInfo[:version]) do |p|
-
# p.package_files = PluginPackageFiles
-
# p.plugin_files = FileList["rails_plugin/**/*"]
-
# p.extra_links = {"Project page"=>ProjectInfo[:homepage],
-
# "Author: #{ProjectInfo[:author_name]}"=>ProjectInfo[:author_link]}
-
# p.verbose = true
-
# end
-
# task :rails_plugin=>:clobber
-
#
-
# desc "Publish Ruby on Rails plug-in on RubyForge"
-
# task :release_plugin=>:rails_plugin do |task|
-
# pub = Rake::SshDirPublisher.new("#{RubyForgeConfig[:user_name]}@rubyforge.org",
-
# "/var/www/gforge-projects/#{RubyForgeConfig[:unix_name]}",
-
# "pkg/rails_plugin")
-
# pub.upload()
-
# end
-
-
-
require 'rake'
-
require 'rake/tasklib'
-
-
module Rake
-
# RailsPluginPackageTask defines the "rails_plugin" task
-
# that recurses through your _package_files_, generates compliant index.html for
-
# each folder (that contains a file), and creates a directory structure that you
-
# can publish as a set for your plugin.
-
#
-
# Noteworthy attributes:
-
#
-
# [package_dir] Directory to store the package. Default 'pkg/rails_plugin'
-
#
-
# [package_dir] Files to include in the plugin.
-
#
-
# [extra_links] Links to put on every generated index page. Can be a hash, e.g.
-
# {"Home"=>"http://roxml.rubyforge.org"}, an array of strings or
-
# a single string.
-
#
-
# [plugin_files] Files to be placed in the root folder of the plug-in, e.g.
-
# init.rb. All files that are in the root of _package_dir_
-
# will also be placed in the root of the plug-in.
-
#
-
class RailsPluginPackageTask < TaskLib
-
# Name of plug-in or application
-
attr_accessor :name
-
# Version of plugin - distribution folder will be name_version
-
attr_accessor :version
-
# Directory used to store the package files (default is 'pkg/rails_plugin').
-
attr_accessor :package_dir
-
# Files to be stored in the package
-
attr_accessor :package_files
-
# Files to go into the root of the plug-in folder (e.g. init.rb)
-
attr_accessor :plugin_files
-
# Homepage for more information
-
attr_accessor :extra_links
-
# Verbose [true | false]
-
attr_accessor :verbose
-
-
# Create the "rails_plugin" task
-
def initialize(name=nil, version=nil)
-
init(name, version)
-
yield self if block_given?
-
define unless name.nil?
-
end
-
-
# Initialize with defaults
-
def init(name, version)
-
@name = name
-
@version = version
-
@extra_links = nil
-
@package_files = Rake::FileList.new
-
@plugin_files = Rake::FileList.new
-
@package_dir = "pkg/"
-
@folders = {}
-
@verbose = false
-
end
-
-
# Define the rails_plugin task
-
def define
-
desc "Create Ruby on Rails plug-in package"
-
task :rails_plugin do
-
@dest = "#@package_dir/#{@name}"
-
makedirs(@dest,:verbose=>false)
-
@plugin_files.each do |fn|
-
cp(fn, @dest,:verbose=>false)
-
add_file(File.basename(fn))
-
end
-
-
@package_files.each do |fn|
-
puts ". #{fn}" if verbose
-
f = File.join(@dest, fn)
-
fdir = File.dirname(f)
-
unless File.exist?(fdir)
-
mkdir_p(fdir,:verbose=>false)
-
add_folder("#{fdir}/")
-
end
-
if File.directory?(fn)
-
mkdir_p(f,:verbose=>false)
-
add_folder("#{fn}/")
-
else
-
cp(fn, f, :verbose=>false)
-
add_file(fn)
-
end
-
end
-
-
generate_index_files()
-
end
-
-
desc 'Remove package files'
-
task :clobber_package do
-
@dest = "#@package_dir"
-
if File.exist?(@dest)
-
FileUtils.remove_dir @dest
-
end
-
end
-
end
-
-
# Generate the index.html files
-
def generate_index_files
-
@folders.each do |folder, files|
-
puts " + Creating #{@dest}/#{folder}/index.html" if @verbose
-
File.open("#{@dest}/#{folder}/index.html", "w") do |index|
-
title = "Rails Plug-in for #@name #@version"
-
index.write("<html><head><title>#{title}</title></head>\n")
-
index.write("<body>\n")
-
index.write("<h2>#{title}</h2>\n")
-
extra_links = create_extra_links()
-
index.write("<p>#{extra_links}</p>\n") if extra_links
-
files.each { |fn|
-
puts(" - Adding #{fn}") if @verbose
-
index.write(" <a href=\"#{fn}\">#{fn}</a><br/>\n")
-
}
-
index.write("<hr size=\"1\"/><p style=\"font-size: x-small\">Generated with RailsPluginPackageTask<p>")
-
index.write("</body>\n")
-
index.write("</html>\n")
-
end
-
end
-
end
-
-
private
-
# Add a file to the folders hash
-
def add_file(filename)
-
dir = File.dirname(filename).gsub("#{@dest}",".")
-
fn = File.basename(filename)
-
folder = @folders[dir] || @folders[dir]=[]
-
folder << fn
-
end
-
-
# Add a folder to the folders hash
-
def add_folder(folder_name)
-
dir = File.dirname(folder_name).gsub("#{@dest}",".").gsub("./","")
-
fn = File.basename(folder_name) + "/"
-
folder = @folders[dir] || @folders[dir]=[]
-
folder << fn
-
end
-
-
# Create the anchor tag for extra links
-
def create_extra_links
-
return nil unless @extra_links
-
x_links = ""
-
if (@extra_links.class==Hash)
-
@extra_links.each do |k,v|
-
x_links << "<a href=\"#{v}\">#{k}</a> "
-
end
-
elsif (@extra_links.class==Array)
-
@extra_links.each do |link|
-
x_links << "<a href=\"#{link}\">#{link}</a> "
-
end
-
else
-
x_links = "<a href=\"#{@extra_links.to_s}\">#{@extra_links.to_s}</a>"
-
end
-
return x_links
-
end
-
end
-
end
-
ProjectInfo = {
-
:name => "acts_as_descriptoable",
-
:description => "Allows model fields and values to be dynamically extended.",
-
:homepage => "http://www.sanger.ac.uk/Users/mw4/ruby/rails/acts_as_descriptable",
-
:version => "1.0",
-
:author_link => "http://www.sanger.ac.uk/Users/mw4/",
-
:author_name => "Matt Wood"
-
}
-
-
ReleaseFiles = FileList[
-
"lib/**/*.rb", "*.txt", "README", "Rakefile", "rakeconfig.rb",
-
"rake/**/*","test/**/*.rb", "*.rb", "test/**/*.xml", "doc/**/*", "html/**/*"
-
].exclude(/\bCVS\b|~$/)
-
-
PluginPackageFiles = FileList[
-
"lib/**/*.rb", "*.txt", "README", "Rakefile", "rakeconfig.rb",
-
"rake/**/*", "test/**/*.rb", "*.rb", "test/**/*.xml"
-
].exclude(/\bCVS\b|~$/)
-
-
# Uninstall hook code here
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module AliquotTagMigration
-
-
class MigratableAliquots < ActiveRecord::Base
-
self.table_name=('aliquots')
-
-
default_scope({
-
:select => 'aliquots.id AS id, lane.id AS lane_id, tags.map_id AS aliquot_index, aliquots.tag2_id AS tag2_id',
-
:joins => [
-
'INNER JOIN assets AS lane ON aliquots.receptacle_id = lane.id',
-
'INNER JOIN tags ON aliquots.tag_id = tags.id'
-
],
-
:conditions => 'lane.sti_type = "Lane"'
-
})
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2014,2015 Genome Research Ltd.
-
1
module ApiTools
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
end
-
-
1
module ClassMethods
-
1
def render_class
-
@render_class ||= Api::Base.render_class_for_model(self)
-
end
-
end
-
-
1
def for_api(options = {})
-
self.class.render_class.to_hash(self)
-
end
-
-
1
def to_xml(options = {})
-
renamed_keys = self.for_api.inject({}) do |renamed_keys,(key,value)|
-
renamed_keys.tap { renamed_keys[key.underscore] = value }
-
end
-
options.reverse_merge!(:root => self.class.to_s.underscore, :skip_types => true)
-
renamed_keys.to_xml(options)
-
end
-
-
1
def list_json(options = {})
-
self.class.render_class.to_hash_for_list(self)
-
end
-
-
# TODO: Add relationships for object
-
1
def as_json(options = {})
-
{ self.json_root => self.class.render_class.to_hash(self), 'lims'=>configatron.amqp.lims_id! }
-
end
-
-
1
def to_yaml(options = {})
-
self.for_api.to_yaml(options)
-
end
-
-
1
def url_name
-
self.class.to_s.underscore
-
end
-
1
alias_method(:json_root, :url_name)
-
-
1
def url
-
[ configatron.api_url, API_VERSION, self.url_name.pluralize, self.uuid ].join('/')
-
end
-
-
end
-
-
1
class ActiveRecord::Base
-
1
include ApiTools
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2013 Genome Research Ltd.
-
1
module AuthenticatedSystem
-
1
protected
-
# Returns true or false if the user is logged in.
-
# Preloads @current_user with the user model if they're logged in.
-
1
def logged_in?
-
3
current_user != :false
-
end
-
-
# Accesses the current user from the session.
-
1
def current_user
-
@current_user ||= (session[:user] && User.find_by_id(session[:user])) || :false
-
end
-
-
# Store the given user in the session.
-
1
def current_user=(new_user)
-
session[:user] = (new_user.nil? || new_user.is_a?(Symbol)) ? nil : new_user.id
-
@current_user = new_user
-
end
-
-
# Check if the user is authorized.
-
#
-
# Override this method in your controllers if you want to restrict access
-
# to only a few actions or if you want to check if the user
-
# has the correct rights.
-
#
-
# Example:
-
#
-
# # only allow nonbobs
-
# def authorize?
-
# current_user.login != "bob"
-
# end
-
1
def authorized?
-
true
-
1
end
-
-
# Filter method to enforce a login requirement.
-
#
-
# To require logins for all actions, use this in your controllers:
-
#
-
# before_filter :login_required
-
#
-
# To require logins for specific actions, use this in your controllers:
-
#
-
# before_filter :login_required, :only => [ :edit, :update ]
-
#
-
# To skip this in a subclassed controller:
-
#
-
# skip_before_filter :login_required
-
#
-
1
def login_required
-
1
username, passwd = get_auth_data
-
-
1
if username && passwd
-
user = User.authenticate(username, passwd)
-
if user.nil?
-
self.current_user = :false
-
else
-
self.current_user = user
-
end
-
1
elsif params[:api_key]
-
user = User.find_by_api_key(params[:api_key])
-
if user.nil?
-
self.current_user = :false
-
else
-
self.current_user = user
-
end
-
end
-
-
1
respond_to do |accepts|
-
2
accepts.html { logged_in? && authorized? ? true : access_denied }
-
1
accepts.csv { logged_in? && authorized? ? true : access_denied }
-
1
if configatron.disable_api_authentication == true
-
1
accepts.xml { true }
-
1
accepts.json { true }
-
else
-
accepts.xml { logged_in? && authorized? ? true : access_denied }
-
accepts.json { logged_in? && authorized? ? true : access_denied }
-
end
-
end
-
end
-
-
1
def admin_login_required
-
setup_current_user
-
respond_to do |accepts|
-
accepts.html { logged_in? && authorized? && current_user.administrator? ? true : access_denied }
-
if configatron.disable_api_authentication == true
-
accepts.xml { true }
-
accepts.json { true }
-
else
-
accepts.xml { logged_in? && authorized? && current_user.administrator? ? true : access_denied }
-
accepts.json { logged_in? && authorized? && current_user.administrator? ? true : access_denied }
-
end
-
end
-
end
-
-
1
def manager_login_required
-
setup_current_user
-
respond_to do |accepts|
-
accepts.html { logged_in? && authorized? && current_user.manager_or_administrator? ? true : access_denied }
-
if configatron.disable_api_authentication == true
-
accepts.xml { true }
-
accepts.json { true }
-
else
-
accepts.xml { logged_in? && authorized? && current_user.manager_or_administrator? ? true : access_denied }
-
accepts.json { logged_in? && authorized? && current_user.manager_or_administrator? ? true : access_denied }
-
end
-
end
-
end
-
-
1
def lab_manager_login_required
-
setup_current_user
-
respond_to do |accepts|
-
accepts.html { logged_in? && authorized? && current_user.lab_manager? ? true : access_denied }
-
if configatron.disable_api_authentication == true
-
accepts.xml { true }
-
accepts.json { true }
-
else
-
accepts.xml { logged_in? && authorized? && current_user.lab_manager? ? true : access_denied }
-
accepts.json { logged_in? && authorized? && current_user.lab_manager? ? true : access_denied }
-
end
-
end
-
end
-
-
1
def slf_manager_login_required
-
setup_current_user
-
respond_to do |accepts|
-
accepts.html { logged_in? && authorized? && (current_user.slf_manager? || current_user.administrator? ) ? true : access_denied }
-
end
-
end
-
-
1
def slf_gel_login_required
-
setup_current_user
-
respond_to do |accepts|
-
accepts.html { logged_in? && authorized? && (current_user.slf_manager? || current_user.slf_gel? || current_user.administrator? ) ? true : access_denied }
-
end
-
end
-
-
1
def setup_current_user
-
username, passwd = get_auth_data
-
self.current_user ||= User.authenticate(username, passwd) || :false if username && passwd
-
end
-
-
# Redirect as appropriate when an access request fails.
-
#
-
# The default action is to redirect to the login screen.
-
#
-
# Override this method in your controllers if you want to have special
-
# behavior in case the user is not authorized
-
# to access the requested action. For example, a popup window might
-
# simply close itself.
-
1
def access_denied
-
respond_to do |accepts|
-
accepts.html do
-
store_location
-
redirect_to :controller => '/sessions', :action => 'login'
-
end
-
accepts.xml do
-
render :xml => {:error => "Couldn't authenticate you"}, :status => :unauthorized
-
end
-
accepts.json do
-
render :json => {:error => "Couldn't authenticate you"}, :status => :unauthorized
-
end
-
end
-
false
-
end
-
-
# Store the URI of the current request in the session.
-
#
-
# We can return to this location by calling #redirect_back_or_default.
-
1
def store_location
-
session[:return_to] = request.original_url
-
end
-
-
# Redirect to the URI stored by the most recent store_location call or
-
# to the passed default.
-
1
def redirect_back_or_default(default)
-
session[:return_to] ? redirect_to(session[:return_to]) : redirect_to(default)
-
session[:return_to] = nil
-
end
-
-
# Inclusion hook to make #current_user and #logged_in?
-
# available as ActionView helper methods.
-
1
def self.included(base)
-
1
base.send :helper_method, :current_user, :logged_in?
-
end
-
-
# When called with before_filter :login_from_cookie will check for an :auth_token
-
# cookie and log the user back in if apropriate
-
1
def login_from_cookie
-
return unless cookies[:auth_token] && !logged_in?
-
user = User.find_by_remember_token(cookies[:auth_token])
-
if user && user.remember_token?
-
user.remember_me
-
self.current_user = user
-
cookies[:auth_token] = { :value => self.current_user.remember_token , :expires => self.current_user.remember_token_expires_at }
-
flash[:notice] = "Logged in successfully"
-
end
-
end
-
-
1
private
-
1
@@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
-
# gets BASIC auth info
-
1
def get_auth_data
-
4
auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
-
1
auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
-
1
return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
-
end
-
end
-
module AuthenticatedTestHelper
-
# Sets the current user in the session from the user fixtures.
-
def login_as(user)
-
@request.session[:user] = user ? user.id : nil
-
end
-
-
def content_type(type)
-
@request.env['Content-Type'] = type
-
end
-
-
def accept(accept)
-
@request.env["HTTP_ACCEPT"] = accept
-
end
-
-
def authorize_as(user)
-
if user
-
@request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{users(user).login}:test")}"
-
accept 'application/xml'
-
content_type 'application/xml'
-
else
-
@request.env["HTTP_AUTHORIZATION"] = nil
-
accept nil
-
content_type nil
-
end
-
end
-
-
# http://study.ioni.st/post/217#post-217
-
#
-
# def test_new_publication
-
# assert_difference(Publication, :count) do
-
# post :create, :publication => {...}
-
# # ...
-
# end
-
# end
-
#
-
def assert_difference(object, method = nil, difference = 1)
-
initial_value = object.send(method)
-
yield
-
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
-
end
-
-
def assert_no_difference(object, method, &block)
-
assert_difference object, method, 0, &block
-
end
-
-
# Assert the block redirects to the login
-
#
-
# assert_requires_login(:bob) { |c| c.get :edit, :id => 1 }
-
#
-
def assert_requires_login(login = nil)
-
yield HttpLoginProxy.new(self, login)
-
end
-
-
def assert_http_authentication_required(login = nil)
-
yield XmlLoginProxy.new(self, login)
-
end
-
-
def reset!(*instance_vars)
-
instance_vars = [:controller, :request, :response] unless instance_vars.any?
-
instance_vars.collect! { |v| "@#{v}".to_sym }
-
instance_vars.each do |var|
-
instance_variable_set(var, instance_variable_get(var).class.new)
-
end
-
end
-
end
-
-
class BaseLoginProxy
-
attr_reader :controller
-
attr_reader :options
-
def initialize(controller, login)
-
@controller = controller
-
@login = login
-
end
-
-
private
-
def authenticated
-
raise NotImplementedError
-
end
-
-
def check
-
raise NotImplementedError
-
end
-
-
def method_missing(method, *args)
-
@controller.reset!
-
authenticate
-
@controller.send(method, *args)
-
check
-
end
-
end
-
-
class HttpLoginProxy < BaseLoginProxy
-
protected
-
def authenticate
-
@controller.login_as @login if @login
-
end
-
-
def check
-
@controller.assert_redirected_to :controller => 'sessions', :action => 'login'
-
end
-
end
-
-
class XmlLoginProxy < BaseLoginProxy
-
protected
-
def authenticate
-
@controller.accept 'application/xml'
-
@controller.authorize_as @login if @login
-
end
-
-
def check
-
@controller.assert_response 401
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Commentable
-
1
def self.included(base)
-
-
4
base.class_eval do
-
4
has_many :comments, :as => :commentable
-
4
scope :with_comments, -> { joins(:comments).where("commentable_type = '#{base.name}'") } do
-
4
def group(ids)
-
conditions = {}
-
if ids
-
conditions[:id]=ids
-
end
-
-
count(:group => :commentable_id, :conditions => conditions)
-
end
-
end
-
4
def self.get_comment_count(ids=nil)
-
h = Hash.new(0) # return 0 if key is not in the hash
-
with_comments.group(ids).each do |commentable_id, comment_count|
-
h[commentable_id.to_i]=comment_count
-
end
-
h
-
end
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012,2015 Genome Research Ltd.
-
1
module ControlRequestTypeCreation
-
1
def control_type_name
-
key_name.titleize
-
end
-
-
1
def find_control_type
-
RequestType.find_by_key(key_name)
-
end
-
-
1
def key_name
-
(last_request_type.key || last_request_type.name.gsub(/\s/,'_').downcase) + '_control'
-
end
-
-
1
def last_request_type
-
@last_request_type ||= self.request_types.last
-
end
-
-
1
def add_control_request_type
-
RequestType.find_or_create_by_key(key_name) do |crt|
-
crt.name = control_type_name
-
crt.request_class_name = 'ControlRequest'
-
crt.multiples_allowed = last_request_type.multiples_allowed
-
crt.initial_state = last_request_type.initial_state
-
crt.asset_type = last_request_type.asset_type
-
crt.order = last_request_type.order
-
crt.request_purpose = RequestPurpose.find_by_key!('control')
-
end.tap do |control_request_type|
-
self.control_request_type = control_request_type
-
end
-
self
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
# Run from Projects - read in plate barcodes from a file, create asset groups in a given project, create submissions for all asset groups in the project
-
-
#get project id
-
print "Project ID? : "
-
project_id = gets.chomp
-
print "Study ID? : "
-
study_id = gets.chomp
-
sample_name_filename="../samplenames"
-
-
sample_names = []
-
myfile = File.open(sample_name_filename)
-
myfile.each do |line|
-
line = line.chomp
-
next if line.blank?
-
sample_names << line
-
end
-
puts "#{sample_names.size} Sample Names read from file"
-
-
asset_group = AssetGroup.create(:name => "#{project_id}_asset_group_#{Time.now}")
-
sample_names.each do |sample_name|
-
sample =Sample.find_by_name(sample_name)
-
raise "Cannot find #{sample_name}" if sample.nil?
-
raise "sample has no asset" if sample.assets.blank?
-
well = sample.assets.first
-
raise "Well not found for sample" unless well.is_a?(Well)
-
asset_group.assets << well
-
end
-
study = Study.find(study_id)
-
study.asset_groups << asset_group
-
study.save
-
-
assets = asset_group.assets
-
asset_list = []
-
assets.each do |asset|
-
asset_list.push asset
-
end
-
-
-
project = Project.find(project_id)
-
submission = LinearSubmission.build(nil, study, project, Submission::Workflow.find(2), User.find_by_login('nts'), asset_list, [], Submission::Workflow.find(2).request_types.map { |r| r.id}, [], [])
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2014 Genome Research Ltd.
-
class ::Sample
-
# This returns all samples that require an accession number to be generated based on the conditions of their
-
# studies and themselves. It comes from a long (and highly frustrating) experience of decoding the
-
# app/models/data_release.rb logic.
-
scope :requiring_accession_number,
-
joins([
-
'INNER JOIN study_samples ON samples.id = study_samples.sample_id',
-
'INNER JOIN studies ON studies.id = study_samples.study_id',
-
'LEFT JOIN sample_metadata AS tcnan_sm ON samples.id = tcnan_sm.sample_id',
-
'LEFT JOIN study_metadata AS trea_sm ON trea_sm.study_id = studies.id',
-
'LEFT JOIN data_release_study_types AS trea_drst ON trea_drst.id = trea_sm.data_release_study_type_id'
-
]).
-
readonly(false).
-
where([ %q{
-
(tcnan_sm.sample_ebi_accession_number IS NULL OR TRIM(tcnan_sm.sample_ebi_accession_number) = '') AND
-
(tcnan_sm.sample_taxon_id IS NOT NULL) AND
-
(tcnan_sm.sample_common_name IS NOT NULL AND TRIM(tcnan_sm.sample_common_name) != '') AND
-
-
trea_sm.data_release_strategy IN (:data_release_managed_or_open) AND
-
studies.enforce_accessioning = TRUE AND NOT (
-
(
-
studies.enforce_data_release = FALSE OR NOT (
-
((trea_sm.data_release_timing IS NULL) OR (trea_sm.data_release_timing IS NOT NULL AND TRIM(trea_sm.data_release_timing) = ''))
-
)
-
) AND (
-
trea_drst.name IN (:data_release_study_type) OR
-
trea_sm.data_release_timing IN (:data_release_timing)
-
)
-
)
-
}, {
-
:data_release_timing => [ 'never', 'delayed' ],
-
:data_release_study_type => DataReleaseStudyType::DATA_RELEASE_TYPES_SAMPLES,
-
:data_release_managed_or_open => [ Study::DATA_RELEASE_STRATEGY_OPEN, Study::DATA_RELEASE_STRATEGY_MANAGED ]
-
} ])
-
end
-
-
# Only ever process those samples that actually need an accession number to be generated for them.
-
current_user = User.find_by_api_key(configatron.accession_local_key) or raise StandardError, "Cannot find accessioning user"
-
Sample.requiring_accession_number.find_each(:include => [ :sample_metadata, { :studies => :study_metadata } ]) do |sample|
-
begin
-
sample.validate_ena_required_fields!
-
sample.accession_service.submit_sample_for_user(sample, current_user) unless sample.accession_service.nil?
-
rescue ActiveRecord::RecordInvalid => exception
-
#warn "Please fill in the required fields for sample: #{sample.name}"
-
rescue AccessionService::NumberNotRequired => exception
-
#warn "An accession number is not required for this study. Study name: #{sample.study.name}"
-
rescue AccessionService::NumberNotGenerated => exception
-
warn 'No accession number was generated'
-
rescue AccessionService::AccessionServiceError => exception
-
warn exception.message
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
-
Plate.requiring_fluidigm_data.find_each do |plate|
-
plate.retrieve_fluidigm_data
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
module DbTableArchiver
-
-
def self.create_archive!
-
puts "Creating archive database: #{archive_name}"
-
ActiveRecord::Base.connection.create_database archive_name
-
end
-
-
def self.archive!(table)
-
table_transaction(table) do |original,archive|
-
puts "Archiving table '#{table}' to #{archive_name}"
-
ActiveRecord::Base.connection.rename_table original, archive
-
end
-
end
-
-
def self.restore!(table)
-
table_transaction(table) do |original,archive|
-
puts "Restoring table '#{table}' from #{archive_name}"
-
ActiveRecord::Base.connection.rename_table archive, original
-
end
-
end
-
-
def self.table_transaction(table)
-
yield "#{ActiveRecord::Base.connection.current_database}.#{table}", "#{ActiveRecord::Base.connection.current_database}_archive.#{table}"
-
end
-
-
def self.archive_name
-
"#{ActiveRecord::Base.connection.current_database}_archive"
-
end
-
-
-
def self.destroy_archive!
-
raise StandardError, "#{archive_name} contains tables. Can't be destroyed!" if ActiveRecord::Base.connection.execute("SHOW tables IN #{archive_name}").present?
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2013,2015 Genome Research Ltd.
-
class EventFactory
-
-
#################################
-
# project related notifications #
-
#################################
-
# Creates an event and sends an email when a new project is created
-
def self.new_project(project, user)
-
content = "Project registered by #{user.login}"
-
-
event = Event.new(
-
:eventful_id => project.id,
-
:eventful_type => "Project",
-
:message => "Project registered",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "administrators"
-
)
-
event.save
-
-
admin_emails = User.all_administrators_emails.reject(&:blank?)
-
-
EventfulMailer.confirm_event(
-
admin_emails,
-
event.eventful,
-
event.message,
-
event.content,
-
"No Milestone"
-
).deliver unless admin_emails.empty?
-
end
-
-
# Creates an event and sends an email or emails when a project is approved
-
def self.project_approved(project, user)
-
content = "Project approved by #{user.login}"
-
-
event = Event.new(
-
:eventful_id => project.id,
-
:eventful_type => "Project",
-
:message => "Project approved",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "administrators"
-
)
-
event.save
-
-
recipients_email = []
-
project_manager_email = ""
-
unless project.manager.blank?
-
project_manager_email = "#{project.manager.email}"
-
recipients_email << project_manager_email
-
end
-
if user.is_administrator?
-
administrators_email = User.all_administrators_emails
-
administrators_email.each do |email|
-
recipients_email << email unless email == project_manager_email
-
end
-
end
-
-
EventfulMailer.confirm_event(recipients_email, event.eventful, event.message, event.content, "No Milestone").deliver
-
end
-
-
################################
-
# Sample related notifications #
-
################################
-
-
# Creates an event and sends an email when a new sample is created
-
def self.new_sample(sample, project, user)
-
content = "New '#{sample.name}' registered by #{user.login}"
-
-
# Create Sample centric event
-
sample_event = Event.create(
-
:eventful_id => sample.id,
-
:eventful_type => "Sample",
-
:message => "Sample registered",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "users"
-
)
-
-
recipients = User.all_administrators_emails
-
-
if project.blank?
-
EventfulMailer.confirm_sample_event(recipients.reject(&:blank?), sample_event.eventful, sample_event.message, sample_event.content, "No Milestone").deliver
-
else
-
# Create project centric event
-
content = "New '#{sample.name}' registered by #{user.login}: #{sample.name}. This sample was assigned to the '#{project.name}' project."
-
-
project_event = Event.create(
-
:eventful_id => project.id,
-
:eventful_type => "Project",
-
:message => "Sample #{sample.name} registered",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "administrators"
-
)
-
-
EventfulMailer.confirm_event(recipients.reject(&:blank?), project_event.eventful, project_event.message, project_event.content, "No Milestone").deliver
-
end
-
-
sample_event
-
end
-
-
def self.project_refund_request(project, user, reference)
-
content = "Refund request by #{user.login}. Reference #{reference}"
-
-
event = Event.new(
-
:eventful_id => project.id,
-
:eventful_type => "Project",
-
:message => "Refund #{reference}",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "administrators"
-
)
-
event.save
-
-
#EventfulMailer.deliver_confirm_event(User.all_administrators_emails, event.eventful, event.message, event.content, "No Milestone")
-
end
-
-
###############################
-
# Study related notifications #
-
###############################
-
-
# creates an event and sends an email when samples are register to a study
-
def self.study_has_samples_registered(study,samples,user)
-
sample_names_string = samples.map{|s| s.name}.join("','")
-
content = "Samples '#{sample_names_string}' registered by user '#{user.login}' on #{Time.now}"
-
-
study_event = Event.create(
-
:eventful_id => study.id,
-
:eventful_type => "Study",
-
:message => "Sample(s) registered",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "users"
-
)
-
-
recipients = []
-
study.projects.each do |project|
-
recipients << project.manager.email if project.manager
-
end
-
-
EventfulMailer.confirm_event(recipients.reject(&:blank?), study_event.eventful, study_event.message, study_event.content, "No Milestone").deliver
-
end
-
-
#################################
-
# request related notifications #
-
#################################
-
-
# creates an event and sends an email when update(s) to a request fail
-
def self.request_update_note_to_manager(request, user, message)
-
content = "#{message}\nwhilst an attempt was made to update request #{request.id}\nby user '#{user.login}' on #{Time.now}"
-
-
request_event = Event.create(
-
:eventful_id => request.id,
-
:eventful_type => "Request",
-
:message => "Request update(s) failed",
-
:created_by => user.login,
-
:content => content,
-
:of_interest_to => "manager"
-
)
-
-
recipients = []
-
request.initial_project.tap do |project|
-
recipients << project.manager.email if project && project.manager
-
end
-
-
EventfulMailer.confirm_event(recipients.reject(&:blank?), request_event.eventful, request_event.message, request_event.content, "No Milestone").deliver
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2015 Genome Research Ltd.
-
class EventfulMailer < ActionMailer::Base
-
def confirm_event(receiver, eventful, message, content, milestone, sent_at = Time.now)
-
mail(
-
:from => "#{configatron.sequencescape_email}",
-
:subject => "#{configatron.mail_prefix} #{eventful.class} #{eventful.id}: #{message}",
-
:bcc => receiver,
-
:body =>{ :eventful => eventful, :message => message, :content => content},
-
:sent_on => sent_at
-
)
-
end
-
-
def update_event(receiver, study, title, content, sent_at = Time.now)
-
mail(
-
:from => "#{configatron.sequencescape_email}",
-
:subject => "#{configatron.mail_prefix} Study #{study.id}: #{title}",
-
:bcc => receiver,
-
:body => {:study => study, :message => title, :content => content},
-
:sent_on => sent_at
-
)
-
end
-
-
def confirm_sample_event(receiver, eventful, message, content, milestone, sent_at = Time.now)
-
mail(
-
:from => "#{configatron.sequencescape_email}",
-
:subject => "#{configatron.mail_prefix} #{eventful.class} #{eventful.id}: #{message}",
-
:bcc => receiver,
-
:body => {:eventful => eventful, :message => message, :content => content},
-
:sent_on => sent_at
-
)
-
end
-
-
def notify_request_fail(receiver, item, request, message, sent_at = Time.now)
-
mail(
-
:from => "#{configatron.sequencescape_email}",
-
:subject => "#{configatron.mail_prefix} Request failure for item #{item.id}",
-
:bcc => receiver,
-
:body => {:item => item, :request => request, :message => message},
-
:sent_on => sent_at
-
)
-
end
-
-
def fail_attempt(receiver, request, sent_at = Time.now)
-
mail(
-
:from => "#{configatron.sequencescape_email}",
-
:subject => "#{configatron.mail_prefix} Attempt fail for #{request.id}",
-
:bcc => receiver,
-
:body => {:request => request},
-
:sent_on => sent_at
-
)
-
end
-
-
def confirm_external_release_event(receiver, eventful, message, content, milestone, sent_at = Time.now)
-
mail(
-
:from => "#{configatron.sequencescape_email}",
-
:subject => "#{configatron.mail_prefix} #{eventful.class} #{eventful.id}: #{message}",
-
:bcc => receiver,
-
:body => {:eventful => eventful, :message => message, :content => content},
-
:sent_on => sent_at
-
)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
module EventfulRecord
-
def has_many_events(&block)
-
3
has_many(:events, :as => :eventful, :dependent => :destroy, :order => 'created_at') do
-
3
def self.event_constructor(name, event_class, event_class_method)
-
line = __LINE__ + 1
-
class_eval(%Q{
-
1
def #{name}(*args)
-
#{event_class.name}.#{event_class_method}(self.proxy_association.owner, *args).tap { |event| self << event unless event.eventful.present? }
-
end
-
}, __FILE__, line)
-
end
-
-
3
class_eval(&block) if block.present?
-
end
-
end
-
-
def has_many_lab_events(&block)
-
3
has_many(:lab_events, :as => :eventful, :dependent => :destroy, :order => 'created_at', &block)
-
end
-
-
def has_one_event_with_family(event_family, &block)
-
has_one(:"#{event_family}_event", :class_name => 'Event', :as => :eventful, :conditions => { :family => event_family }, :order => 'id DESC', &block)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module SingleSignOn
-
1
class ServerError < Exception
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module ExternalProperties
-
-
1
def get_external_value(key)
-
key = key.to_s
-
-
# that wil load all the properties , which is faster if we access more than one property
-
# and if we pre-load them with eager loaging
-
external_properties.each do |property|
-
return property.value if property.key == key
-
end
-
return nil
-
end
-
-
1
def self.included(base)
-
2
base.send(:has_many, :external_properties, :as => :propertied, :dependent => :destroy)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
module ExternalResource
-
ResourceName = "SNP"
-
-
def self.included(base)
-
base.send(:has_one, :identifier, :as => :external)
-
end
-
-
def set_identifiable(ident)
-
ident.set_external(ResourceName, self)
-
end
-
-
alias identifiable= set_identifiable
-
-
def identifiable
-
identifier and identifier.identifiable
-
end
-
-
def identifiable_id
-
identifier and identifier.identifiable.id
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013 Genome Research Ltd.
-
module FluidigmHelper
-
-
def self.map_configuration_for(width,height,plate_layout)
-
wells = []
-
size = width*height
-
digit_count = Math.log10(size+1).ceil
-
height.times do |r|
-
width.times do |c|
-
wells << {
-
:description => "S%0#{digit_count}d" % [(width*r)+c+1],
-
:location_id => ((width*r)+c+1),
-
:asset_size => size,
-
:row_order => ((width*r)+c),
-
:column_order => ((height*c)+r),
-
:asset_shape_id => plate_layout
-
}
-
end
-
end
-
wells
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014,2015 Genome Research Ltd.
-
module ForeignKeyConstraint
-
-
def add_constraint(table,modl,options={})
-
parse_options(table,modl,options) do |table,modl,as,fk|
-
say "Creating foreign key constraint between #{table}.#{as} and #{modl}.#{fk}"
-
connection.execute("ALTER TABLE #{table} ADD CONSTRAINT fk_#{table}_to_#{modl} FOREIGN KEY (#{as}) REFERENCES #{modl} (#{fk});")
-
end
-
end
-
-
def drop_constraint(table,modl,options={})
-
parse_options(table,modl,options) do |table,modl,as,fk|
-
say "Dropping foreign key constraint between #{table}.#{as} and #{modl}.#{fk}"
-
connection.execute("ALTER TABLE #{table} DROP FOREIGN KEY fk_#{table}_to_#{modl};")
-
end
-
end
-
-
def parse_options(table,modl,options)
-
fk = options[:foreign_key]||'id'
-
as = options[:as]||"#{modl.singularize}_id"
-
raise 'Invalid table name' unless /\A[a-z0-9_]+\Z/===table
-
raise 'Invalid model name' unless /\A[a-z0-9_]+\Z/===modl
-
raise 'Invalid foreign key' unless /\A[a-z0-9_]+\Z/===fk
-
raise 'Invalid association' unless /\A[a-z0-9_]+\Z/===as
-
yield(table,modl,as,fk)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
class ApiGenerator < Rails::Generator::NamedBase
-
def banner
-
"Usage: #{$0} #{spec.name} ModelName [create] [update]"
-
end
-
-
def singular_human_name
-
self.singular_name.gsub(/_/, ' ')
-
end
-
-
def plural_human_name
-
self.plural_name.gsub(/_/, ' ')
-
end
-
-
def can_create?
-
actions.include?('create')
-
end
-
-
def can_update?
-
actions.include?('update')
-
end
-
-
def belongs_to_associations
-
reflections(:belongs_to) do |name|
-
{ :json => name.to_s }
-
end
-
end
-
-
def has_many_associations
-
reflections(:has_many) do |name|
-
{ :json => name.to_s, :to => name.to_s, :include => [] }
-
end
-
end
-
-
IGNOREABLE_ATTRIBUTES = [ :id, :created_at, :updated_at ]
-
-
def attributes
-
# Reject any attributes that are either automatically output or belong to an association.
-
names = model.column_names.map(&:to_sym).reject(&IGNOREABLE_ATTRIBUTES.method(:include?)).map(&:to_s)
-
names = names - model.reflections.keys.map { |c| "#{c}_id" }
-
-
# Now ensure that the justification is such that it fits the nice output format!
-
max_length_name = names.map(&:size).max
-
names.map { |s| [ s.rjust(max_length_name), s ] }
-
end
-
-
def reflections(type, &block)
-
model.reflections.map do |name, reflection|
-
case
-
when name.to_sym == :uuid_object then nil
-
when reflection.macro != type.to_sym then nil
-
else [ name.to_sym, yield(name) ]
-
end
-
end.compact.sort { |(a,_),(b,_)| a.to_s <=> b.to_s }
-
end
-
private :reflections
-
-
def model
-
singular_name.classify.constantize
-
end
-
-
def manifest
-
record do |manifest|
-
manifest.directory("app/api/endpoints")
-
manifest.directory("app/api/io")
-
manifest.directory("app/api/model_extensions")
-
manifest.directory("features/api")
-
-
manifest.template('endpoint.rb', "app/api/endpoints/#{plural_name}.rb")
-
manifest.template('io.rb', "app/api/io/#{singular_name}.rb")
-
manifest.template('extension.rb', "app/api/model_extensions/#{singular_name}.rb")
-
manifest.template('cucumber.feature', "features/api/#{plural_name}.feature")
-
-
manifest.readme('WHAT-TO-DO-NEXT')
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
class ::Endpoints::<%= plural_name.camelize %> < ::Core::Endpoint::Base
-
model do
-
-
<% if can_create? -%>
-
action(:create) do |request, response|
-
request.create!
-
end
-
<% end -%>
-
end
-
-
instance do
-
<% belongs_to_associations.each do |name, options| -%>
-
belongs_to(<%= name.to_sym.inspect %>, <%= options.map { |k,v| "#{k.inspect} => #{v.inspect}" }.join(', ') %>)
-
<% end -%>
-
<% has_many_associations.each do |name, options| -%>
-
has_many(<%= name.to_sym.inspect %>, <%= options.map { |k,v| "#{k.inspect} => #{v.inspect}" }.join(', ') %>)
-
<% end -%>
-
-
<% if can_update? -%>
-
action(:update) do |request, response|
-
request.update!
-
end
-
<% end -%>
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# TODO: All of the behaviour in this file should really exist within the <%= class_name %> model.
-
module ModelExtensions::<%= class_name %>
-
def self.included(base)
-
base.class_eval do
-
# TODO: Add an associations or named_scopes required
-
end
-
end
-
-
# TODO: Add any instance methods required
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
class ::Io::<%= class_name %> < ::Core::Io::Base
-
set_model_for_input(::<%= class_name %>)
-
set_json_root(:<%= singular_name %>)
-
# set_eager_loading { |model| model } # TODO: uncomment and add any named_scopes that do includes you need
-
-
# TODO: define the mapping from the model attributes to the JSON attributes
-
#
-
# The rules are relatively straight forward with each line looking like '<attibute> <access> <json>', and blank lines or
-
# those starting with '#' being considered comments and ignored.
-
#
-
# Here 'access' is either '=>' (for read only, indicating that the 'attribute' maps to the 'json'), or '<=' for write only (yes,
-
# there are cases for this!) or '<=>' for read-write.
-
#
-
# The 'json' is the JSON attribute to generate in dot notation, i.e. 'parent.child' generates the JSON '{parent:{child:value}}'.
-
#
-
# The 'attribute' is the attribute to write, i.e. 'name' would be the 'name' attribute, and 'parent.name' would be the 'name'
-
# attribute of whatever 'parent' is.
-
define_attribute_and_json_mapping(%Q{
-
<% attributes.each do |left_side, right_side| -%>
-
<%= left_side %> => <%= right_side %>
-
<% end -%>
-
})
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
class TaskGenerator < Rails::Generator::NamedBase
-
def banner
-
"Usage: #{$0} #{spec.name} ModelName"
-
end
-
-
def model
-
singular_name.classify.constantize
-
end
-
-
def manifest
-
record do |manifest|
-
manifest.directory("app/models/tasks")
-
manifest.directory("app/views/workflows")
-
manifest.directory("db/migrate")
-
-
manifest.migration_template("migration.rb", "db/migrate", :migration_file_name => "add_#{singular_name}_task")
-
manifest.template('handler.rb', "app/models/tasks/#{singular_name}_handler.rb")
-
manifest.template('task.rb', "app/models/#{singular_name}_task.rb")
-
manifest.template('task_view.html.erb', "app/views/workflows/_#{singular_name}_batches.html.erb")
-
-
-
-
manifest.readme('WHAT-TO-DO-NEXT')
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
module Tasks::<%= singular_name.camelize %>Handler
-
def render_<%= singular_name %>_task(task, params)
-
end
-
-
def do_<%= singular_name %>_task(task, params)
-
true
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) <%= '2015'; DateTime.now.year %> Genome Research Ltd.
-
class Add<%= singular_name.camelize %>Task < ActiveRecord::Migration
-
def self.up
-
# Fill in the workflow and the ordering of the task (sorted)
-
workflow = LabInterface::Workflow.find_by_name('Fill in the workflow')
-
<%= singular_name.camelize %>Task.create!( :name => '<%= singular_name.gsub(/_/,' ') %>', :sorted => 1, :batched => true, :workflow => workflow )
-
end
-
-
def self.down
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
class <%= singular_name.camelize %>Task < Task
-
class <%= singular_name.camelize %>Data < Task::RenderElement
-
def initialize(request)
-
super(request)
-
end
-
end
-
-
def create_render_element(request)
-
request.asset && <%= singular_name.camelize %>Data.new(request)
-
end
-
-
def partial
-
"<%= singular_name %>_batches"
-
end
-
-
def render_task(workflow, params)
-
workflow.render_<%= singular_name %>_task(self, params)
-
end
-
-
def do_task(workflow, params)
-
workflow.do_<%= singular_name %>_task(self, params)
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2013,2014 Genome Research Ltd.
-
1
module Hiseq2500Helper
-
-
1
def self.create_request_type(pl, ended='paired')
-
RequestType.create!(
-
:key => "illumina_#{pl}_hiseq_2500_#{ended}_end_sequencing",
-
:name => "Illumina-#{pl.upcase} HiSeq 2500 #{ended.titleize} end sequencing",
-
:workflow => Submission::Workflow.find_by_key('short_read_sequencing'),
-
:asset_type => 'LibraryTube',
-
:order => 2,
-
:initial_state => 'pending',
-
:multiples_allowed => true,
-
:request_class_name => 'HiSeqSequencingRequest',
-
:product_line => ProductLine.find_by_name("Illumina-#{pl.upcase}")
-
)
-
end
-
-
1
def self.template(settings)
-
{
-
:name => settings[:name],
-
:product_line => ProductLine.find_by_name("Illumina-#{settings[:pipeline].upcase}"),
-
:submission_class_name => "LinearSubmission",
-
:submission_parameters=> {
-
:workflow_id => 1,
-
:request_type_ids_list => request_types(settings),
-
:info_differential => 1
-
}.merge(other(settings))
-
}
-
end
-
-
1
def self.sequencing_request_type(settings)
-
RequestType.find_by_key("illumina_#{settings[:pipeline]}_hiseq_2500_#{settings[:ended]||'paired'}_end_sequencing")
-
end
-
-
1
def self.library_request_type(settings)
-
# Ugh, our production and seeded database differ
-
RequestType.find_by_key(settings[:library_creation].first)||RequestType.find_by_key(settings[:library_creation].last)
-
end
-
-
1
def self.request_types(settings)
-
rts = settings[:cherrypick] ? [[RequestType.find_by_key(settings[:cherrypick]).id]] : []
-
rts << [library_request_type(settings).id] << [sequencing_request_type(settings).id]
-
end
-
-
1
def self.input_fields(sizes,libraries)
-
[
-
FieldInfo.new(:kind => "Text",:default_value => "",:parameters => {},:display_name => "Fragment size required (from)",:key => "fragment_size_required_from"),
-
FieldInfo.new(:kind => "Text",:default_value => "",:parameters => {},:display_name => "Fragment size required (to)",:key => "fragment_size_required_to"),
-
FieldInfo.new(
-
:kind => "Selection",:default_value => "Standard",:parameters => { :selection => libraries },
-
:display_name => "Library type",
-
:key => "library_type"
-
),
-
FieldInfo.new(:kind => "Selection",:default_value => sizes.last,:parameters => {:selection => sizes},:display_name => "Read length",:key => "read_length")
-
]
-
end
-
-
1
def self.other(settings)
-
case settings[:sub_params]
-
when :ill_c
-
{ }
-
when :ill_c_single
-
{ }
-
when :sc
-
{:request_options=>{"fragment_size_required_to"=>"400", "fragment_size_required_from"=>"100", "library_type"=>"Agilent Pulldown"}}
-
when :wgs
-
{:request_options=>{"fragment_size_required_to"=>"500", "fragment_size_required_from"=>"300", "library_type"=>"Standard"}}
-
when :ill_b
-
{ }
-
when :ill_b_single
-
{ }
-
else
-
raise "Invalid submission parameters"
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2014 Genome Research Ltd.
-
1
module Identifiable
-
1
def self.included(base)
-
3
base.send(:has_many, :identifiers, :as => :identifiable)
-
3
base.instance_eval do
-
3
scope :with_identifier, ->(t) { {
-
:include => :identifiers,
-
:conditions => { :identifiers => { :resource_name => t } }
-
} }
-
-
3
scope :sync_identifier, ->(t) { {
-
:joins => "INNER JOIN identifiers sid ON sid.identifiable_id=samples.id AND sid.identifiable_type IN (#{[self,*self.descendants].map(&:name).map(&:inspect).join(',')})",
-
:conditions => ['sid.resource_name=? AND NOT sid.do_not_sync AND sid.external_id IS NOT NULL', t]
-
} }
-
end
-
end
-
-
1
def identifier(resource_name)
-
identifiers.detect { |i| i.resource_name == resource_name }
-
end
-
-
1
def set_external(resource_name, object_or_id)
-
raise Exception.new, "Resource name can't be blank" if resource_name.blank?
-
ident = identifier(resource_name) || identifiers.build(:resource_name => resource_name)
-
if object_or_id.is_a? Fixnum
-
ident.external_id = object_or_id
-
else
-
ident.external = object_or_id
-
end
-
ident.save
-
# ident.save!
-
end
-
-
1
def external_id(resource_name)
-
ident = identifier(resource_name)
-
ident ? ident.external_id : nil
-
end
-
-
1
def external_object(resource_name)
-
ident = identifier(resource_name)
-
ident ? ident.external : nil
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
#require 'active_support'
-
module ImportPulledData
-
# import into the database a list of object from a yaml file
-
def self.import_from_yaml(file_name, names_map = {})
-
object_parameters = YAML::load(File.read(file_name))
-
import_from_parameters(object_parameters, names_map)
-
end
-
-
# create and save object from a parameters list
-
# each parameter is a hash
-
# :class => class name
-
# :id => id
-
# :attributes => a has with the attributes
-
def self.import_from_parameters(object_parameters, names_map = {})
-
object_parameters.map do |parameter|
-
klass = parameter[:class].constantize
-
object_id = parameter[:id]
-
attributes = parameter[:attributes]
-
# map name from table
-
if name=attributes["name"]
-
attributes["name"] = names_map.fetch(name, name)
-
end
-
-
object = klass.new(attributes) { |r| r.id = object_id }
-
if object.respond_to? :save_after_unmarshalling
-
object.save_after_unmarshalling
-
else
-
object.save(validate: false)
-
end
-
end
-
end
-
-
end
-
module Informatics
-
module Globals
-
-
@@application = nil
-
@@defaults = nil
-
-
def application
-
@@application
-
end
-
-
def application=(app)
-
@@application = app
-
end
-
-
def defaults
-
@@defaults
-
end
-
-
def defaults=(incoming)
-
@@defaults = incoming
-
end
-
-
def global_searchable_classes
-
[ Project, Study, Sample, Asset, AssetGroup, Request, Supplier ]
-
end
-
-
def search_options
-
global_searchable_classes.map {|klass| [klass.name]*2 } << ['All',nil]
-
end
-
-
end
-
end
-
namespace :deploy do
-
namespace :mongrel do
-
-
task :start, :roles => :app do
-
run "mongrel_rails mongrel::start -C #{shared_path}/config/server.yml -c #{current_path}"
-
end
-
-
task :restart, :roles => :app do
-
run "mongrel_rails mongrel::restart -c #{current_path}"
-
end
-
-
task :stop, :roles => :app do
-
run "mongrel_rails mongrel::stop -c #{current_path}"
-
end
-
-
end
-
-
namespace :cluster do
-
-
task :start, :roles => :app do
-
run "mongrel_rails cluster::start -C #{shared_path}/config/server.yml"
-
end
-
-
task :restart, :roles => :app do
-
run "mongrel_rails cluster::restart -C #{shared_path}/config/server.yml"
-
end
-
-
task :stop, :roles => :app do
-
run "mongrel_rails cluster::stop -C #{shared_path}/config/server.yml"
-
end
-
-
end
-
-
# TODO - staging hardcoded in path for LogRotate, intended?
-
namespace :logrotate_tasks do
-
task :force, :roles => :app do
-
run "/usr/sbin/logrotate -f -s /software/webapp/staging/logrotate.status #{shared_path}/config/logrotate.conf"
-
end
-
end
-
-
desc "Disable requests to the app, show maintenance page"
-
task :disable_web, :roles => :app do
-
run "cp #{current_path}/public/maintenance.html #{shared_path}/system/maintenance.html"
-
end
-
-
desc "Re-enable the web server by deleting any maintenance file"
-
task :enable_web, :roles => :app do
-
run "rm #{shared_path}/system/maintenance.html"
-
end
-
-
desc "Custom restart task for mongrel cluster"
-
task :restart, :roles => :app, :except => { :no_release => true } do
-
deploy.cluster.restart
-
end
-
-
desc "Custom start task for mongrel cluster"
-
task :start, :roles => :app do
-
deploy.cluster.start
-
end
-
-
desc "Custom stop task for mongrel cluster"
-
task :stop, :roles => :app do
-
deploy.cluster.stop
-
end
-
-
desc "Force rotation of logfiles"
-
task :logrotate, :roles => :app do
-
deploy.logrotate_tasks.force
-
end
-
-
end
-
load "vendor/plugins/informatics/lib/informatics/globals"
-
load "vendor/plugins/informatics/lib/informatics/deployment"
-
load "vendor/plugins/informatics/deploy/services"
-
load "vendor/plugins/informatics/deploy/mongrel"
-
1
require File.dirname(__FILE__)+'/globals'
-
1
require File.dirname(__FILE__)+'/support/options'
-
-
1
module Informatics
-
1
class Application
-
-
1
include Informatics::Globals
-
-
1
attr_accessor :name, :description, :home_page, :title, :authentication
-
-
1
def self.configure
-
a = self.new
-
yield a
-
@@application = a
-
end
-
-
1
def named(name)
-
@name = name
-
end
-
-
1
def authenticate_via(type)
-
@authentication = type
-
end
-
-
1
def subtitle(t)
-
@title = t
-
end
-
-
1
def described_as(desc)
-
@description = desc
-
end
-
-
1
def home_page_link(link)
-
@home_page = link
-
end
-
-
end
-
end
-
-
1
require File.dirname(__FILE__)+'/view/menu/item'
-
1
require File.dirname(__FILE__)+'/view/menu/list'
-
1
require File.dirname(__FILE__)+'/view/tabs/item'
-
1
require File.dirname(__FILE__)+'/view/tabs/list'
-
-
1
module ApplicationHelper
-
-
1
include Informatics::Globals
-
-
1
def add(type, link, options = nil)
-
21
o = Informatics::Support::Options.collect(options)
-
21
l = Informatics::Support::Options.collect(link)
-
21
case type
-
when :menu
-
@menu = Informatics::View::Menu::List.new unless @menu
-
13
@menu = add_link(@menu, l, o, options)
-
when :back_menu
-
@back_menu = Informatics::View::Menu::List.new unless @back_menu
-
@back_menu.add_item :text => l.first_key, :link => l.first_value
-
when :about # Replaces :title
-
@about = link
-
when :title # This option is deprecated in favour of :about as that is how it was getting used
-
@about = link
-
when :lab_option
-
@lab_menu = add_link(@lab_menu, l, o, options)
-
when :lab_manager_option
-
@lab_manager_menu = add_link(@lab_manager_menu, l, o, options)
-
when :admin_option
-
@admin_menu = add_link(@admin_menu, l, o, options)
-
when :manager_option
-
@manager_menu = add_link(@manager_menu, l, o, options)
-
when :banner
-
@banner = link
-
when :legend_option
-
@legend = add_link(@legend, l, o, options)
-
when :tab
-
@tabs = Informatics::View::Tabs::List.new unless @tabs
-
8
@tabs.add_item :text => l.first_key, :link => l.first_value
-
end
-
end
-
-
1
def logger
-
Rails.logger
-
end
-
-
1
private
-
-
1
def add_link(menu, l, o, options)
-
13
menu = Informatics::View::Menu::List.new unless menu
-
13
unless options.nil?
-
if o.key_is_present?(:confirm)
-
if o.key_is_present?(:method)
-
menu.add_item :text => l.first_key, :link => l.first_value, :confirm => o.value_for(:confirm), :method => o.value_for(:method)
-
else
-
menu.add_item :text => l.first_key, :link => l.first_value, :confirm => o.value_for(:confirm)
-
end
-
end
-
else
-
13
menu.add_item :text => l.first_key, :link => l.first_value
-
end
-
13
menu
-
end
-
-
end
-
1
module Informatics
-
1
class Deployment
-
-
1
attr_accessor :cap, :app_name, :deploy_name, :repository_location, :log_location, :config_location, :nginx_location
-
1
attr_accessor :service_uri, :service_port, :balanced_ports, :html_root, :nginx_binaries
-
-
1
def self.configure(options)
-
a = self.new
-
c = options[:with]
-
a.cap = c
-
yield a
-
-
# Set deployment variable in Capistrano::Configure to make it available
-
# to the rest of the deployment scripts
-
c.set :deployment, a
-
end
-
-
1
def start_nginx_from(l)
-
@nginx_binaries = l
-
cap.set :nginx_binaries, l
-
end
-
-
1
def root(h)
-
@html_root = h
-
end
-
-
1
def balanced_against(a)
-
puts "SETTING BALANCED PORTS: #{a.to_s}"
-
@balanced_ports = a
-
end
-
-
1
def balanced_ports
-
puts "GETTING BALANCED PORTS: #{@balanced_ports.to_s}"
-
@balanced_ports
-
end
-
-
1
def accessible_at(u)
-
@service_uri = u
-
end
-
-
1
def on(p)
-
@service_port = p
-
end
-
-
1
def nginx_config
-
parsed("vendor/plugins/informatics/assets/deployment/nginx.conf.erb")
-
end
-
-
1
def mime_types
-
parsed("vendor/plugins/informatics/assets/deployment/mime.types.erb")
-
end
-
-
1
def mongrel_config
-
parsed("vendor/plugins/informatics/assets/deployment/server.yml.erb")
-
end
-
-
1
def logrotate_config
-
parsed("vendor/plugins/informatics/assets/deployment/logrotate.conf.erb")
-
end
-
-
1
def nginx_files(f)
-
@nginx_location = f
-
end
-
-
1
def configuration(c)
-
@config_location = c
-
end
-
-
1
def named(n)
-
@app_name = n
-
cap.set :application, n
-
end
-
-
1
def log_to(l)
-
@log_location = l
-
end
-
-
1
def deploy_as(n)
-
@deploy_name = n
-
cap.set :deploy_name, n
-
end
-
-
1
def repository(r)
-
@repository_location = r
-
cap.set :repository, r
-
end
-
-
1
private
-
-
1
def parsed(filename)
-
ERB.new(File.new(filename, "r").read).result(binding)
-
end
-
-
end
-
end
-
1
module Informatics
-
1
module Globals
-
-
1
@@application = nil
-
1
@@defaults = nil
-
-
1
def application
-
@@application
-
end
-
-
1
def application=(app)
-
@@application = app
-
end
-
-
1
def defaults
-
@@defaults
-
end
-
-
1
def defaults=(incoming)
-
@@defaults = incoming
-
end
-
-
1
def global_searchable_classes
-
1
[ Project, Study, Sample, Asset, AssetGroup, Request, Supplier ]
-
end
-
-
1
def search_options
-
8
global_searchable_classes.map {|klass| [klass.name]*2 } << ['All',nil]
-
end
-
-
end
-
end
-
1
module Informatics
-
1
module Support
-
1
class Options
-
-
1
attr_accessor :options
-
-
1
def self.collect(*options)
-
42
o = self.new
-
42
o.options = options
-
42
o
-
end
-
-
1
def options=(opt)
-
42
if opt.is_a? Array
-
42
opt = opt[0]
-
end
-
42
@options = opt
-
end
-
-
1
def options
-
@options
-
end
-
-
1
def first_key
-
21
incoming_options.keys.first
-
end
-
-
1
def first_value
-
21
incoming_options.values.first
-
end
-
-
1
def key_is_present?(key)
-
incoming_options.key? key
-
end
-
-
1
def value_for(key)
-
incoming_options[key]
-
end
-
-
1
private
-
-
1
def incoming_options
-
42
o = nil
-
42
if @options.is_a? Hash
-
42
o = @options
-
elsif @options.is_a? Array
-
o = @options[0]
-
end
-
42
o
-
end
-
-
1
def logger
-
Rails.logger
-
end
-
-
end
-
end
-
end
-
module Informatics
-
module User
-
class Settings
-
-
include Informatics::Globals
-
-
attr_accessor :keys
-
-
def self.available
-
d = self.new
-
yield d
-
@@defaults = d
-
end
-
-
def add(key, value)
-
unless @keys
-
@keys = {}
-
end
-
@keys[key] = value
-
end
-
-
def method_missing(m, *a)
-
@keys.each do |key, value|
-
if key.to_s == m.to_s
-
return value
-
end
-
end
-
raise NoMethodError, "#{m}"
-
end
-
-
end
-
end
-
end
-
1
module Informatics
-
1
module View
-
1
module Menu
-
1
class Item
-
-
1
attr_accessor :text, :link, :confirm, :method
-
-
1
def initialize(options = {})
-
13
@text = options[:text]
-
13
@link = options[:link]
-
13
@confirm = options[:confirm]
-
13
@method = options[:method]
-
end
-
-
end
-
end
-
end
-
end
-
1
module Informatics
-
1
module View
-
1
module Menu
-
-
1
class List
-
-
1
attr_accessor :items
-
-
1
def add_item(options = {})
-
13
if !@items
-
13
@items = []
-
end
-
13
@items.push Informatics::View::Menu::Item.new({ :text => options[:text], :link => options[:link], :method => options[:method], :confirm => options[:confirm] })
-
end
-
-
end
-
-
end
-
end
-
end
-
1
module Informatics
-
1
module View
-
1
module Tabs
-
-
1
class Item
-
-
1
attr_accessor :text, :link
-
-
1
def initialize(options = {})
-
8
@text = options[:text]
-
8
@link = options[:link]
-
end
-
-
end
-
-
end
-
end
-
end
-
1
module Informatics
-
1
module View
-
1
module Tabs
-
-
1
class List
-
-
1
attr_accessor :items
-
-
1
def initialize(options = {})
-
1
@items = []
-
end
-
-
1
def add_item(options)
-
8
@items.push Informatics::View::Tabs::Item.new({ :text => options[:text], :link => options[:link] })
-
end
-
-
end
-
-
end
-
end
-
end
-
# Informatics gem
-
# Models, views and controller helpers for Informatics applications.
-
-
# Load and require necessary files
-
1
$:.unshift File.dirname(__FILE__)
-
5
Dir["lib/informatics/lib/informatics/*.rb"].each { |format| require "informatics/#{File.basename format}" }
-
# Uninstall hook code here
-
13
<% if item.link == '' %>
-
3
<li><%= item.text.html_safe %></li>
-
10
<% elsif item.confirm.nil? -%>
-
10
<li><%= link_to item.text.html_safe, item.link %></li>
-
<% else %>
-
<% if item.method.nil? -%>
-
<li><%= link_to item.text.html_safe, item.link, :confirm => item.confirm %></li>
-
<% else -%>
-
<li><%= link_to item.text.html_safe, item.link, :method => item.method, :confirm => item.confirm %></li>
-
<% end -%>
-
<% end -%>
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
require 'rest-client'
-
-
module LabWhereClient
-
-
LabwhereException = Class.new(StandardError)
-
-
class LabWhere
-
-
def base_url
-
configatron.fetch(:labwhere_api)
-
end
-
-
def path_to(instance, target)
-
raise LabwhereException, "LabWhere service URL not set" if base_url.nil?
-
[base_url, instance.endpoint, target].compact.join('/')
-
end
-
-
def parse_json(str)
-
return nil if str=='null'
-
JSON.parse(str)
-
rescue JSON::ParserError => e
-
raise LabwhereException.new(e), "LabWhere is returning unexpected content", e.backtrace
-
end
-
-
def get(instance, target)
-
parse_json(RestClient.get(path_to(instance,target)))
-
rescue Errno::ECONNREFUSED => e
-
raise LabwhereException.new(e), "LabWhere service is down", e.backtrace
-
end
-
-
def post(instance, target, payload)
-
parse_json(RestClient.post(path_to(instance,target), payload))
-
rescue Errno::ECONNREFUSED => e
-
raise LabwhereException.new(e), "LabWhere service is down", e.backtrace
-
rescue RestClient::UnprocessableEntity => e
-
return parse_json(e.response)
-
end
-
-
def put(instance, target, payload)
-
parse_json(RestClient.put(path_to(instance,target), payload))
-
rescue Errno::ECONNREFUSED => e
-
raise LabwhereException.new(e), "LabWhere service is down", e.backtrace
-
end
-
-
end
-
-
class Endpoint
-
-
def self.endpoint_name(name)
-
@endpoint = name
-
end
-
-
class << self
-
attr_reader :endpoint
-
end
-
-
def initialize(params)
-
end
-
-
end
-
-
module EndpointCreateActions
-
module ClassMethods
-
def creation_params(params)
-
params
-
end
-
-
def create(params)
-
attrs = LabWhere.new.post(self, nil, creation_params(params))
-
new(attrs) unless attrs.nil?
-
end
-
end
-
def self.included(base)
-
base.send(:extend, ClassMethods)
-
end
-
end
-
-
module EndpointUpdateActions
-
module ClassMethods
-
def update(target, params)
-
attrs = LabWhere.new.put(self, target, params)
-
new(attrs) unless attrs.nil?
-
end
-
end
-
def self.included(base)
-
base.send(:extend, ClassMethods)
-
end
-
end
-
-
class Labware < Endpoint
-
endpoint_name 'labwares'
-
-
attr_reader :barcode
-
attr_reader :location
-
-
def self.find_by_barcode(barcode)
-
return nil if barcode.blank?
-
attrs = LabWhere.new.get(self, barcode)
-
new(attrs) unless attrs.nil?
-
end
-
-
def initialize(params)
-
@barcode = params['barcode']
-
@location = Location.new(params['location'])
-
end
-
end
-
-
class Scan < Endpoint
-
include EndpointCreateActions
-
-
attr_reader :message, :errors
-
-
endpoint_name 'scans'
-
-
def initialize(params)
-
@message = params['message']
-
@errors = params['errors']
-
end
-
-
def response_message
-
@message
-
end
-
-
def self.creation_params(params)
-
obj = params.dup
-
obj[:labware_barcodes] = obj[:labware_barcodes].join("\n")
-
{ :scan => obj }
-
end
-
-
def valid?
-
@errors.nil?
-
end
-
-
def error
-
@errors.join(";")
-
end
-
-
end
-
-
class Location < Endpoint
-
endpoint_name 'locations'
-
-
attr_reader :name
-
attr_reader :parentage
-
-
def initialize(params)
-
@name = params['name']
-
@parentage = params['parentage']
-
end
-
-
def location_info
-
[parentage, name].join(' - ')
-
end
-
end
-
-
class LocationType < Endpoint
-
endpoint_name 'location_types'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
require "xml/libxml"
-
-
class XML::Node
-
##
-
# Open up XML::Node from libxml and add convenience methods inspired
-
# by hpricot.
-
# (http://code.whytheluckystiff.net/hpricot/wiki/HpricotBasics)
-
# Also:
-
# * provide better handling of default namespaces
-
-
# an array of default namespaces to past into
-
attr_accessor :default_namespaces
-
-
# find the child node with the given xpath
-
def at(xpath)
-
self.find_first(xpath)
-
end
-
-
# find the array of child nodes matching the given xpath
-
def search(xpath)
-
results = self.find(xpath).to_a
-
if block_given?
-
results.each do |result|
-
yield result
-
end
-
end
-
return results
-
end
-
-
# alias for search
-
def /(xpath)
-
search(xpath)
-
end
-
-
# return the inner contents of this node as a string
-
def inner_xml
-
child.to_s
-
end
-
-
# alias for inner_xml
-
def inner_html
-
inner_xml
-
end
-
-
# return this node and its contents as an xml string
-
def to_xml
-
self.to_s
-
end
-
-
# alias for path
-
def xpath
-
self.path
-
end
-
-
# provide a name for the default namespace
-
def register_default_namespace(name)
-
self.namespace.each do |n|
-
if n.to_s == nil
-
register_namespace("#{name}:#{n.href}")
-
return
-
end
-
end
-
raise "No default namespace found"
-
end
-
-
# register a namespace, of the form "foo:http://example.com/ns"
-
def register_namespace(name_and_href)
-
(@default_namespaces ||= []) <<name_and_href
-
end
-
-
def find_with_default_ns(xpath_expr, namespace=nil)
-
find_base(xpath_expr, namespace || default_namespaces)
-
end
-
-
def find_first_with_default_ns(xpath_expr, namespace=nil)
-
find_first_base(xpath_expr, namespace || default_namespaces)
-
end
-
-
-
alias_method :find_base, :find unless method_defined?(:find_base)
-
alias_method :find, :find_with_default_ns
-
-
alias_method :find_first_base, :find_first unless method_defined?(:find_first_base)
-
alias_method :find_first, :find_first_with_default_ns
-
end
-
-
class String
-
def to_libxml_doc
-
xp = XML::Parser.new
-
xp.string = self
-
return xp.parse
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011 Genome Research Ltd.
-
num_pico_plates = PicoDilutionPlate.count
-
-
stock_plates = Plate.find(:all, :conditions => "sti_type = 'Plate'", :limit => num_pico_plates)
-
count = 0
-
PicoDilutionPlate.all.each do |pico_plate|
-
AssetLink.create_edge!(stock_plates[count],pico_plate)
-
count = count +1
-
end
-
-
1
module ManifestUtil
-
1
def is_end_of_header?(row, pos)
-
((pos!=(row.length-1)) && row[pos].blank? && row[pos+1].blank?)
-
end
-
-
1
def filter_end_of_header(header_row)
-
found_end_of_header = false
-
header_row.reject.each_with_index do |value, pos|
-
found_end_of_header ||= is_end_of_header?(header_row, pos)
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
class MetadataMigration < ActiveRecord::Migration
-
class Property < ActiveRecord::Base
-
class Definition < ActiveRecord::Base
-
self.table_name =('property_definitions')
-
has_many :properties, :class_name => 'MetadataMigration::Property', :foreign_key => :property_definition_id, :dependent => :destroy
-
-
scope :for_class, ->(c) { { :conditions => { :relates_to => c } } }
-
scope :for_keys, ->(keys) { { :conditions => { :key => keys } } }
-
-
# It's more efficient to delete all of the properties and then delete the definition.
-
def self.delete_for(relates_to, keys)
-
definition_ids = self.for_class(relates_to).for_keys(keys).all.map(&:id)
-
Property.delete_all([ 'property_definition_id IN (?)', definition_ids ])
-
self.delete_all([ 'id IN (?)', definition_ids ])
-
end
-
end
-
-
self.table_name =('properties')
-
belongs_to :definition, :class_name => 'MetadataMigration::Property::Definition', :foreign_key => :property_definition_id
-
end
-
-
def self.reference_class_name
-
self.reference_class.name.sub(/^[^:]+::/, '')
-
end
-
-
def self.properties_from_metadata
-
metadata_class.column_names.reject do |name|
-
[ :id, self.reference_id ].include?(name.to_sym)
-
end.inject({}) do |hash,p|
-
returning(hash) do
-
hash[ p ] = Property::Definition.first(
-
:conditions => { :relates_to => self.reference_class_name, :key => p.to_s }
-
) or raise StandardError, "Cannot find property definition for '#{ p }'"
-
end
-
end
-
end
-
-
# Migrates all of the property instances to their related metadata instances
-
def self.migrate_properties
-
properties = self.properties_from_metadata
-
-
say("There are #{ reference_class.count } records to process")
-
-
start = 0
-
reference_class.find_in_batches(:batch_size => 1500, :include => :properties) do |records|
-
say_with_time("Processing #{start}-#{start + records.length}") do
-
# Create new objects that can be validated.
-
objects = records.map do |record|
-
metadata_class.new(
-
properties.inject({ self.reference_id.to_s => record.id }) do |attributes,(property,definition)|
-
returning(attributes) do
-
attributes[ property.to_s ] = record.properties.detect { |p|
-
p.property_definition_id == definition.id
-
}.try(:value)
-
end
-
end
-
)
-
end
-
-
# If there is a single record that fails validation try saving each one. That way the InvalidRecord error
-
# will be raised and we'll know which one failed! Otherwise we can bulk save these.
-
objects.map(&:save!) unless objects.all?(&:valid?)
-
metadata_class.import(objects)
-
end
-
-
start += records.length
-
end
-
end
-
-
def self.up
-
ActiveRecord::Base.transaction do
-
self.create_metadata_table
-
begin
-
say('Migrating all of the properties (this might take a very long time!)')
-
self.metadata_class.reset_column_information
-
self.migrate_properties
-
-
# Delete all of the properties that we have migrated, leaving any that may exist outside that.
-
say('Destroying all of the migrated property definitions')
-
Property::Definition.delete_for(self.reference_class_name, self.metadata_class.column_names.reject do |name|
-
[ :id, self.reference_id ].include?(name.to_sym)
-
end)
-
rescue
-
self.drop_table(self.metadata_class.table_name)
-
raise
-
end
-
end
-
rescue ActiveRecord::RecordInvalid => exception
-
$stderr.puts "Invalid record: #{ exception.record.inspect }"
-
raise
-
end
-
-
def self.down
-
raise ActiveRecord::IrreversibleMigration, 'Cannot reverse property to metadata migration'
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module ActiveResource::Formats::PacBioJsonFormat
-
1
class << self
-
1
def decode(json)
-
ActiveSupport::JSON.decode(json)["Rows"]
-
end
-
1
def extension
-
"json"
-
end
-
1
def encode(hash, options = nil)
-
ActiveSupport::JSON.encode(hash, options)
-
end
-
1
def mime_type
-
"application/json"
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
class PipelineException < Exception
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
#!/usr/bin/env ruby
-
# Make sure stdout and stderr write out without delay for using with daemon like scripts
-
STDOUT.sync = true; STDOUT.flush
-
STDERR.sync = true; STDERR.flush
-
-
#Try to Load Merb
-
merb_init_file = File.expand_path(File.dirname(__FILE__)+'/../config/merb_init')
-
if File.exists? merb_init_file
-
require File.expand_path(File.dirname(__FILE__)+'/../config/boot')
-
#need this because of the CWD
-
Merb.root = MERB_ROOT
-
require merb_init_file
-
else
-
# Load Rails
-
Rails.root ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
-
require File.join(Rails.root, 'config', 'boot')
-
require File.join(Rails.root, 'config', 'environment')
-
end
-
-
# Load ActiveMessaging processors
-
#ActiveMessaging::load_processors
-
-
# Start it up!
-
ActiveMessaging::start
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
1
module PolymorphicAttributable
-
-
1
def self.included(base)
-
1
base.class_eval do
-
1
def self.set_polymorphic_attributes(name, options = {})
-
camel_name = name.to_s.camelize
-
self.class_eval <<EOR
-
-
def #{name}
-
case
-
when self.material.nil?
-
nil
-
when self.material.is_a?(#{camel_name})
-
self.material
-
else
-
nil
-
end
-
end
-
-
def #{name}=(name_instance)
-
if name_instance
-
raise ArgumentError, "Can't assing a \#{name_instance.class} to a #{camel_name}" unless name_instance.is_a?(#{camel_name})
-
end
-
self.material = name_instance
-
end
-
-
-
def #{name}_id
-
material_id if self.material_type == "#{camel_name}"
-
end
-
-
def #{name}_id= (name_id)
-
self.material_id = name_id
-
self.material_type = #{camel_name}
-
name_id
-
end
-
EOR
-
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module ProductHelpers
-
def self.single_template(name)
-
{
-
:name => name,
-
:selection_behaviour => 'SingleProduct',
-
:products => {
-
nil => name
-
}
-
}
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012 Genome Research Ltd.
-
# Mixin providing interface to proxy creation and proxy support.
-
1
module Proxyable
-
1
def self.included(base)
-
1
base.extend ClassMethods
-
end
-
-
1
def proxy
-
unless @proxy
-
@proxy = self.class.new_proxy(self)
-
end
-
-
@proxy
-
end
-
-
# Class Methods for the mixed class
-
1
module ClassMethods
-
# defines the proxy class. Override to use a specific customized ResourceProxy
-
# Note than there is another mechanism to define specific for a nested resource
-
# @return (Class<=ResourceProxy)
-
1
def proxy_class
-
ResourceProxy
-
end
-
-
1
def new_proxy_list(object_list = [])
-
object_list.map { |o| new_proxy(o) }
-
end
-
-
# Create a new proxy for an object from partial description of it.
-
# @param [String, Integer, Object] object
-
# @return ResourceProxy
-
1
def new_proxy(object)
-
proxy_class.new(object, self)
-
end
-
-
# This method load all object from a proxy list. Skip it if the first object is already loaded
-
1
def load_proxy_list(proxy_list, options = {})
-
return if proxy_list.empty?
-
-
force = options.delete(:force)
-
return if proxy_list.first.loaded? && ( ! force )
-
-
ids_to_proxies = proxy_list.index_by(&:id)
-
results = self.find(ids_to_proxies.keys, options)
-
results.each { |object| ids_to_proxies[ object.id ].set_object(object) }
-
results
-
end
-
end
-
end
-
#!/usr/bin/env script/runner
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2011,2012 Genome Research Ltd.
-
require 'optparse'
-
require 'rgl/dot'
-
DOT=RGL::DOT
-
-
# Hack to work different version of sequencescape
-
begin
-
Aliquot
-
-
Asset
-
class Asset
-
def sample
-
nil
-
end
-
-
def tags
-
[]
-
end
-
-
end
-
rescue
-
class Aliquot < ActiveRecord::Base
-
class Receptacle < Asset
-
end
-
end
-
-
Asset
-
class Asset
-
def aliquots
-
[]
-
end
-
end
-
end
-
-
begin
-
TagInstance
-
rescue
-
class TagInstance < Asset
-
end
-
end
-
class TagInstance < Asset
-
def sample
-
nil
-
end
-
end
-
#TODO put in a module
-
class Renderer
-
def render(objects, options, &block)
-
render_objects(objects.walk_objects(options, &block))
-
end
-
end
-
-
class ScriptRenderer < Renderer
-
def render_object(objects)
-
objects.map do |object|
-
att = object_to_hash(object)
-
%Q{
-
#{object.class.name}.new(#{att.inspect}) { |r| r.id = #{object.id} }.save(validate: false)
-
}
-
end
-
end
-
end
-
-
-
class GraphRenderer < Renderer
-
def initialize(rankdir="LR")
-
@rankdir = rankdir
-
end
-
-
def layouts
-
Layouts[:submission]
-
end
-
def render(objects, options, &block)
-
@root_object = Set.new(objects)
-
options = [options] unless options.is_a?(Array)
-
-
nodes, edges = options.inject([objects, []]) do |ne, option|
-
nodes, old_edges = ne
-
new_edges = nodes.walk_objects(option, &block)
-
new_nodes = extract_used_objects_from_edges(new_edges)
-
-
[new_nodes, old_edges + new_edges]
-
end
-
-
construct_graph(nodes, edges).to_s
-
end
-
-
def extract_used_objects_from_edges(edges)
-
nodes = Set.new()
-
edges.each do |edge|
-
nodes << edge.parent
-
nodes << edge.object unless edge.is_a?(HiddenEdge) or edge.is_a?(Ellipsis) or edge.is_a?(CutEdge)
-
#nodes << edge.object unless edge.is_a?(HiddenEdge) or edge.is_a?(Ellipsis)
-
end
-
return nodes.to_a.compact
-
end
-
-
def construct_graph(nodes, edges)
-
DOT::Graph.new("rankdir" =>@rankdir).tap do |graph|
-
-
-
layouts.each do |layout|
-
#layout.transform!(nodes, edges).each { |e| graph << e }
-
end
-
-
drawn_nodes = add_nodes(graph, nodes, edges)
-
add_edges(graph, drawn_nodes, edges)
-
-
#hack
-
all_requests = drawn_nodes.select { |n| n.is_a?(Request)}
-
all_requests = []
-
-
all_requests.group_by(&:class).each do |klass, requests|
-
subgraph = DOT::Subgraph.new("name" => "cluster_#{klass.name}", "style"=>"filled", "fillcolor"=>"gold", "color" => "goldenrod4")
-
requests.each do |r|
-
subsubgraph = DOT::Subgraph.new("name" => "cluster_#{r.node_name}", "color"=>"none")
-
[r.asset.requests.size <=1 ? r.asset : nil, r, r.target_asset].compact.each do |o|
-
subsubgraph << DOT::Node.new("name" => o.node_name)
-
graph << subsubgraph
-
end
-
end
-
graph << subgraph
-
end
-
end
-
end
-
-
def add_nodes(graph, major_nodes, edges)
-
#normal node
-
hidden = Set.new()
-
normal = Set.new()
-
cut = Set.new()
-
ellipsis = Set.new()
-
label_map = {}
-
-
edges.each do |edge|
-
-
set = case edge
-
when Ellipsis then ellipsis
-
when HiddenEdge then hidden
-
when CutEdge then cut
-
else normal
-
end
-
set << edge.object
-
label_map[edge.object] ||= edge.label if edge.object
-
end
-
-
cut += ellipsis
-
cut -= normal
-
hidden -= (cut + normal)
-
-
done = Set.new()
-
-
-
# Draw each node
-
edges.each do |edge|
-
[edge.parent, edge.object].each do |node|
-
next if done.include?(node) or not node
-
done << node
-
node_options = node.try(:node_options)
-
if label=label_map[node]
-
node_options["label"] = label
-
end
-
dot_node = case
-
when normal.include?(node)
-
DOT::Node.new(node_options.merge("style" => "filled"))
-
when ellipsis.include?(node)
-
DOT::Node.new(node_options.merge("color" => "gray", "fontcolor" => "gray", "shape" => "roundbox"))
-
when cut.include?(node)
-
DOT::Node.new(node_options.merge("color" => "gray"))
-
end
-
next unless dot_node
-
if @root_object.include?(node)
-
dot_node.options.merge!( "penwidth" => "5")
-
end
-
graph << dot_node
-
end
-
end
-
-
return cut+normal
-
end
-
-
def add_edges(graph, nodes, edges)
-
edges.each do |edge|
-
next unless edge.parent and edge.object
-
next if edge.is_a?(HiddenEdge) and not nodes.include?(edge.object)
-
-
graph << create_edge(edge)
-
end
-
end
-
-
def create_edge(edge)
-
dot_edge = DOT::Edge.new(edge.edge_options)
-
dot_edge.from = edge.parent.node_name
-
dot_edge.to = edge.object.node_name
-
return dot_edge
-
end
-
end
-
-
# remove doulbon
-
class SingleGraphRenderer < GraphRenderer
-
def add_edges(graph, nodes, edges)
-
edge_set = Set.new()
-
super(graph, nodes, edges.select { |e| edge_set.include?(e.key) == false and edge_set<< e.key and edge_set.include?(e.reversed_key) == false})
-
end
-
end
-
-
class DigraphRenderer < GraphRenderer
-
def construct_graph(nodes, edges)
-
DOT::Digraph.new("rankdir" => "LR").tap do |graph|
-
-
add_nodes(graph, nodes, edges)
-
add_edges(graph, nodes, edges)
-
end
-
end
-
-
def create_edge(edge)
-
dot_edge = DOT::DirectedEdge.new(edge.edge_options)
-
dot_edge.from = edge.parent.node_name
-
dot_edge.to = edge.object.node_name
-
return dot_edge
-
end
-
end
-
class YamlRenderer < Renderer
-
def render_objects(objects)
-
objects.map do |object|
-
att = object_to_hash(object)
-
{:class => object.class.name, :id => object.id, :attributes => att}
-
end.to_yaml
-
end
-
end
-
-
$options = {:output_method => YamlRenderer.new, :model => :sample_with_assets}
-
$objects = []
-
$already_pulled = {}
-
-
class Edge
-
attr_reader :parent, :object, :index, :max_index
-
def initialize(*args)
-
@parent, @object, @index, @max_index = args
-
#if @parent and @parent.is_a?(Request)
-
#request = @parent
-
#@name = request.class.name
-
#if @object == request.asset
-
#@parent = request.target_asset
-
#elsif @object == request.target_asset
-
#@parent = request.asset
-
#end
-
#elsif @object and @object.is_a?(Request) and (not @parent or @parent.is_a?(Asset))
-
#request = @object
-
#@name = request.class.name
-
#if @parent == request.asset
-
#@object = request.target_asset
-
#elsif parent == request.target_asset
-
#@object = request.asset
-
#end
-
#end
-
end
-
-
def label()
-
if index
-
"#{index+1}/#{max_index+1} - #{object.class.name}# #{object.id}"
-
else
-
"#{object.class.name}# #{object.id}"
-
end
-
end
-
-
def node_options
-
{
-
"name" => object.node_name,
-
"label" => label(),
-
"style" => "filled"
-
}
-
end
-
-
def edge_options
-
case
-
# Request related
-
when object && object.is_a?(Request) && parent && parent.is_a?(Asset)
-
{ "color" => object.color, "style" => "bold", "dir" => "both"}.merge(
-
if object.asset == parent # source side
-
{ "taillabel" => "", "arrowtail" => "dot", "arrowhead" => "empty", "sametail" => "source_#{parent.node_name}"}
-
else
-
{ "taillabel" => "", "arrowtail" => "normal", "arrowhead" => "odot", "sametail" => "target_#{parent.node_name}"}
-
end
-
)
-
when parent.is_a?(Request) && object && object.is_a?(Asset)
-
{ "color" => parent.color, "style" => "bold", "dir" => "both"}.merge(
-
if parent.asset == object # source side
-
{"headlabel" => "", "arrowtail" => "empty", "arrowhead" => "dot", "samehead" => "source_#{object.node_name}"}
-
else
-
{"headlabel" => "", "arrowhead" => "normal", "arrowtail" => "odot", "sametail" => "target_#{parent.node_name}"}
-
end
-
)
-
# AssetLink
-
when object.is_a?(Asset) && parent.is_a?(Asset)
-
if parent and parent.parents.present? and parent.parents.include?(object)
-
@parent, @object = [@object, @parent] # reverse so they could share the same 'sametail'
-
end
-
{"style" => "dashed", "dir" => "both"}.merge(
-
{"arrowtail" => "odiamond", "arrowhead" => "empty", "sametail" => parent.node_name, "samehead" => object.node_name}
-
)
-
#Aliquot
-
when object.is_a?(Aliquot) && parent.is_a?(Asset)
-
{"arrowtail" => "odiamond", "arrowhead" => "empty", "sametail" => parent.node_name, "samehead" => object.node_name, "dir" => "both"}
-
when parent.is_a?(Aliquot) && object.is_a?(Asset)
-
@parent, @object = [@object, @parent] # reverse so they could share the same 'sametail'
-
{"arrowtail" => "odiamond", "arrowhead" => "empty", "sametail" => parent.node_name, "samehead" => object.node_name, "dir"=> "both"}
-
else
-
{}
-
end
-
end
-
-
# TODO proably a ruby way to do it
-
def key
-
[parent, object]
-
end
-
-
# the key of the reversed edge, not necessarily the reverse of the key
-
def reversed_key
-
[object, parent]
-
end
-
-
end
-
-
class CutEdge < Edge
-
def node_options
-
super.merge({ "color" => "red", "style" => "none"})
-
end
-
-
def edge_options
-
super.merge("constraint" => "true", "color" => "gray")
-
end
-
end
-
-
class HiddenEdge < CutEdge
-
end
-
-
class Ellipsis < CutEdge
-
def label
-
#"#{[max_index+1-3, 1].max} x #{object.class.name}"
-
" ... x #{object.class.name}"
-
end
-
end
-
-
class NodeToCluster
-
def initialize(*args)
-
@classes = args
-
end
-
#we could return new nodes and edges, but we modify them instead ... evil
-
def transform!(nodes, edges)
-
clusters = []
-
selected_nodes = nodes.select { |n| @classes.any? { |c| n.is_a?(c) } }
-
-
#create a cluster for each node
-
selected_nodes.each do | node|
-
cluster_name = "luster_#{node.node_name}"
-
cluster = DOT::Subgraph.new("name" => cluster_name, "label" => node.node_name, "fillcolor" => "yellow", "color" => "lightcyan", "style"=>"filled")
-
cluster << DOT::Node.new("name" => node.node_name)
-
clusters << cluster
-
#node.instance_eval "def node_name(); '#{cluster_name}'; end"
-
-
edges.each do |edge|
-
next if edge.is_a?(HiddenEdge)
-
#debugger if edge.parent == node
-
-
#i edge.parent == node and edge.object and nodes.include?(edge.object) #!edge.is_a?(HiddenEdge)
-
if edge.parent == node and edge.object and !edge.is_a?(HiddenEdge)
-
cluster << DOT::Node.new("name" => edge.object.node_name)
-
else if edge.object == node and edge.parent and !edge.is_a?(HiddenEdge)
-
cluster << DOT::Node.new("name" => edge.parent.node_name)
-
end
-
edges.delete(edge)
-
end
-
end
-
-
nodes.delete(node)
-
end
-
clusters
-
end
-
end
-
-
-
# Model descriptions can be specified for subclass.
-
# use :skip_super to not include superclass association
-
Models = {
-
:submission => [{
-
Submission => [:study, :project, RequestByType=->(s) { s.requests.group_by(&:request_type_id).values }, :asset_group],
-
Request => [:asset, :target_asset],
-
Asset => [->(s) { s.requests.group_by(&:request_type_id).values }, :requests_as_target, :children, :parent],
-
AssetGroup => [:assets]
-
},
-
{
-
Request => [:submission]
-
}
-
],
-
-
:simple_submission => { Submission => ->(s) { s.requests.group_by(&:request_type_id).values }} ,
-
-
:asset_down => AssetDown={ Asset => [:children, RequestByType, :sample, :tags],
-
Request => [:target_asset],
-
Submission => [RequestByType],
-
Aliquot::Receptacle => [:aliquots],
-
Aliquot => [:sample, :tag],
-
Plate => [:wells]
-
},
-
:asset_up => AssetUp={ Asset => [:parents, :requests_as_target],
-
TagInstance => [:tag],
-
Request => [:asset],
-
Aliquot => [:sample, :tag],
-
Aliquot::Receptacle => [:aliquots],
-
Well => [:plate]},
-
:sample_with_assets => AssetDown.merge(
-
Sample => [:assets, :study_samples],
-
StudySample => [:study],
-
Asset => [:requests, :children, :parents],
-
Well => [:container_association],
-
ContainerAssociation => [:container, :content] ,
-
Request => [:submission, :asset,:item, :target_asset, :request_metadata, :user],
-
Submission => [:asset_group]
-
),
-
:asset_up_and_down => [AssetUp, AssetDown],
-
:asset_down_and_up => [AssetDown, AssetUp],
-
:full_asset => AssetUp.merge(AssetDown),
-
:asset => [{Asset => [ ->(s) { s.requests.group_by(&:request_type_id).values },
-
:requests_as_target, :children, :parents]},
-
{ Request => [:asset, :target_asset]}],
-
:submission_down => [{ Submission => RequestByType}.merge(AssetDown)] ,
-
:bare => {}
-
}
-
-
Layouts = { :submission => [ NodeToCluster.new(Submission)
-
]
-
}
-
optparse = OptionParser.new do |opts|
-
opts.on('-e', '--eval expr ', 'ruby expression returning a list of object to pull') do |expr|
-
$objects<< expr
-
end
-
opts.on('-s', '--sample id_or_name', 'sample to pull') do |sample|
-
$objects<< [Sample, sample]
-
end
-
-
opts.on('--study id_or_name', 'study to pull') do |study|
-
$objects<< [Study, study]
-
end
-
-
opts.on('-a', '--asset id_or_name', 'asset to pull') do |asset|
-
$objects<< [Asset, asset]
-
end
-
-
opts.on('-a', '--request id', 'request to pull') do |request|
-
$objects<< [Request, request]
-
end
-
-
opts.on('--submission id_or_name', 'submission to pull') do |submission|
-
$objects << [Submission, submission]
-
end
-
-
opts.on('-rt','--request_type', 'request types') do |request_types|
-
$objects<< [RequestType, request_types]
-
end
-
-
opts.on '-m' '--model', 'model of what needs to be pull' do |model_name|
-
$options[:model]=model_name
-
end
-
opts.on('-rs', '--ruby', 'Generate a ruby script') do
-
$options[:output_method] = ScriptRenderer.new
-
end
-
-
opts.on('-g', '--graph type', 'Generate a dot graph') do |type|
-
$options[:output_method] = SingleGraphRenderer.new
-
set_graph_filter_option(type)
-
end
-
opts.on('--graph_down type', 'Generate a dot graph') do |type|
-
$options[:output_method] = SingleGraphRenderer.new("TB")
-
set_graph_filter_option(type)
-
end
-
opts.on('-d', '--digraph type', 'Generate a dot graph') do |type|
-
$options[:output_method] = DigraphRenderer.new
-
set_graph_filter_option(type)
-
end
-
end
-
-
def set_graph_filter_option(type)
-
$options[:block] = case type
-
when "full"
-
Proc.new do |object, parent, index, max_index|
-
Edge.new(parent, object, index, max_index)
-
end
-
when "3max"
-
Proc.new do |object, parent, index, max_index|
-
if index == 1 and max_index > 1
-
# things been removed
-
RubyWalk::Cut.new(Ellipsis.new(parent, object, index, max_index))
-
else
-
Edge.new(parent, object, index, max_index) if [0,max_index].include?(index) or not parent
-
end
-
end
-
when "3center"
-
Proc.new do |object, parent, index, max_index|
-
# cut everything but one
-
kept_index = [1, max_index || 0].min
-
kept_index = index unless parent
-
case index || kept_index
-
when kept_index
-
Edge.new(parent, object, index, max_index)
-
when 0,max_index
-
# things been removed
-
#RubyWalk::Cut.new({ parent => [object, object.class.name]})
-
RubyWalk::Cut.new(CutEdge.new(parent,object, index, max_index))
-
when 2
-
#RubyWalk::Cut.new({ parent => [object, "..."]})
-
RubyWalk::Cut.new(Ellipsis.new(parent,object, index, max_index))
-
else
-
RubyWalk::Cut.new(HiddenEdge.new(parent, object, index, max_index))
-
end
-
end
-
end
-
end
-
-
def load_objects(objects)
-
loaded = []
-
objects.each do |object|
-
case object
-
when Array
-
model, name = object
-
name.split(",").each do |name|
-
if name =~ /\A\d+\Z/
-
#name is an id
-
object = model.find_by_id(name)
-
elsif name =~ /\A:(first|last)\Z/
-
object = model.find(name)
-
else
-
object = model.find_by_name(name)
-
end
-
-
raise RuntimeError, "can't find #{model} '#{name}'" unless object
-
loaded << object
-
end
-
when String
-
loaded = eval(object)
-
end
-
end
-
return loaded
-
end
-
-
def object_to_hash(object)
-
att =object.attributes.reject { |k,v| [ :created_at, :updated_at ].include?(k.to_sym) }
-
att.each do |k,v|
-
if k =~ /name|login|email|decription|abstract|title/i and v.is_a?(String) and (k !~ /class_?name/)
-
att[k] = "#{object.class.name}_#{object.id}_#{k}"
-
end
-
end
-
att.reject { |k,v| !v }
-
end
-
-
def find_model(model_name)
-
model = Models[model_name.to_sym]
-
raise "can't find model #{model_name}. Available models are #{Models.keys.inspect}" unless model
-
model
-
end
-
-
ARGV.shift # to remove the -- needed using script/runner
-
optparse.parse!
-
#puts $options.to_yaml
-
#puts $objects.to_yaml
-
#
-
-
#---------------
-
# reopening existing class for graph
-
-
-
class ActiveRecord::Base
-
def node_name()
-
"#{self.class}##{self.id}"
-
end
-
def node_options()
-
{ "name" => node_name }
-
end
-
end
-
-
[Submission, Project,Study, Tag].each do |klass|
-
klass.class_eval do
-
#include ProjectLike
-
def node_options_with_lyellow()
-
node_options_without_lyellow.merge("shape" => "note", "fillcolor" => "lightyellow")
-
end
-
alias_method_chain :node_options, :lyellow
-
end
-
end
-
-
[Asset].each do |klass|
-
klass.class_eval do
-
def node_options_with_lcyan()
-
node_options_without_lcyan.merge("fillcolor" => "lightcyan")
-
end
-
alias_method_chain :node_options, :lcyan
-
end
-
end
-
[Well].each do |klass|
-
klass.class_eval do
-
def node_options_with_well()
-
node_options_without_well.merge("shape" => "septagon")
-
end
-
alias_method_chain :node_options, :well
-
end
-
end
-
[Plate].each do |klass|
-
klass.class_eval do
-
def node_options_with_parallel()
-
node_options_without_parallel.merge("shape" => "parallelogram")
-
end
-
alias_method_chain :node_options, :parallel
-
end
-
end
-
[PulldownMultiplexedLibraryTube, MultiplexedLibraryTube, LibraryTube].each do |klass|
-
klass.class_eval do
-
def node_options_with_invt()
-
node_options_without_invt.merge("shape" => "invtrapezium")
-
end
-
alias_method_chain :node_options, :invt
-
end
-
end
-
[AssetGroup].each do |klass|
-
klass.class_eval do
-
def node_options_with_tab()
-
node_options_without_tab.merge("shape" => "tab")
-
end
-
alias_method_chain :node_options, :tab
-
end
-
end
-
-
[Request].each do |klass|
-
klass.class_eval do
-
def node_options_with_rect()
-
node_options_without_rect.merge("shape" => "rectangle")
-
end
-
alias_method_chain :node_options, :rect
-
end
-
end
-
-
begin
-
TagInstance
-
rescue
-
class TagInstance
-
def node_options
-
{}
-
end
-
end
-
end
-
[TagInstance, Sample, Tag].each do |klass|
-
klass.class_eval do
-
def node_options_with_fillcolor()
-
node_options_without_fillcolor.merge("fillcolor" => "darkorange")
-
end
-
alias_method_chain :node_options, :fillcolor
-
end
-
end
-
[Aliquot].each do |klass|
-
klass.class_eval do
-
def node_options_with_cyan()
-
node_options_without_cyan.merge("fillcolor" => tag ? "deepskyblue3" : "dodgerblue")
-
end
-
alias_method_chain :node_options, :cyan
-
end
-
end
-
[Well, MultiplexedLibraryTube].each do |klass|
-
klass.class_eval do
-
def node_options_with_multi
-
node_options_without_multi.merge("fillcolor" => aliquots.size > 1 ? "red": "lightcyan")
-
end
-
alias_method_chain :node_options, :multi
-
end
-
end
-
class Request
-
def color()
-
{"passed" => "green4", "failed" => "firebrick4", "pending" => "dodgerblue4", "started" => "darkorchid4"}.fetch(state, "gray22")
-
end
-
def node_options_with_state_color()
-
node_options_without_state_color.merge("fontcolor" => color() )
-
end
-
alias_method_chain :node_options, :state_color
-
end
-
-
class Asset
-
-
has_many :requests_as_target, :class_name => 'Request', :foreign_key => :target_asset_id, :include => :request_metadata
-
end
-
-
-
-
puts $options[:output_method].render(load_objects($objects), find_model($options[:model]), &($options[:block]))
-
#!/usr/bin/env ruby
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
require File.dirname(__FILE__) + "/../config/environment"# unless defined?(Rails.root)
-
#require 'activemessaging'
-
-
script_name = File.split(__FILE__).last
-
$0 = script_name.chomp(".rb") if $0.include?("script/runner")
-
-
A13G_CONF = ActiveMessaging::Gateway::load_connection_configuration
-
A13G_QUEUE_NAME = ActiveMessaging::Gateway::subscriptions["qc_evaluations_processor:qc_evaluations"].destination.value
-
# conf => {:reconnectDelay=>5, :host=>"localhost", :login=>"", :port=>61613, :adapter=>:stomp, :reliable=>true, :passcode=>""}
-
stomp_client = Stomp::Client.new(
-
A13G_CONF[:login],
-
A13G_CONF[:passcode],
-
A13G_CONF[:host],
-
A13G_CONF[:port],
-
A13G_CONF[:reliable]
-
)
-
-
# Require client to explicitly acknowledge
-
stomp_headers = {
-
:ack => "client",
-
"activemq.prefetchSize" => 1,
-
"activemq.exclusive" => true,
-
}
-
-
puts "Subscription: '#{A13G_QUEUE_NAME}'"
-
nr_messages = 0
-
stomp_client.subscribe A13G_QUEUE_NAME, stomp_headers do |msg|
-
# processor thread:
-
# content in msg.body,
-
# stomp_client.acknowledge(msg) on success,
-
# stomp_client.unreceive(msg) for DLQ
-
begin
-
# pp msg.body
-
doc = Hash.from_xml(msg.body)
-
Batch.qc_evaluations_update(doc["evaluations"])
-
stomp_client.acknowledge(msg)
-
Rails.logger.silence(Logger::INFO) do
-
Rails.logger.warn("Processed OK, #{doc['evaluations']['evaluation'].length} QC evaluations.", script_name)
-
end
-
rescue ActiveRecord::StatementInvalid, Mysql::Error => e
-
Rails.logger.warn("#{e.message} -- will keep on trying...", script_name)
-
sleep 10 # to allow the DBMS to come back (might just be restarting or whatever)
-
begin
-
ActiveRecord::Base.verify_active_connections!
-
Rails.logger.warn("Reconnected to DBMS.", script_name)
-
retry
-
rescue => e
-
Rails.logger.error("#{e.message} -- failed to reconnect DB, sorry, giving up.", script_name)
-
raise
-
end
-
rescue NoMethodError => e
-
Rails.logger.warn("Failed to parse message (#{msg.body.length} bytes), skipping: #{msg.body} --- #{e.message} (#{e.backtrace[0]})", script_name)
-
#stomp_client.acknowledge(msg)
-
stomp_client.unreceive(msg)
-
rescue => e
-
Rails.logger.warn("Error, rejecting message #{e.message}", script_name)
-
stomp_client.unreceive(msg)
-
end
-
# if( (nr_messages += 1) > 100 )
-
# Rails.logger.info("Clean exit after #{nr_messages - 1} messages.", script_name)
-
# Thread.exit
-
# end
-
end
-
stomp_client.join
-
stomp_client.close
-
# Shamelessly derived from Rick Olsen's acts_as_attachment
-
class RoleModelGenerator < Rails::Generator::NamedBase
-
default_options :skip_migration => false
-
-
def manifest
-
record do |m|
-
# Check for class naming collisions.
-
m.class_collisions class_path, class_name, "#{class_name}Test"
-
-
# Model, test, and fixture directories.
-
m.directory File.join('app/models', class_path)
-
m.directory File.join('test/unit', class_path)
-
m.directory File.join('test/fixtures', class_path)
-
-
# Model class, unit test, and fixtures.
-
m.template 'model.rb', File.join('app/models', class_path, "#{file_name}.rb")
-
m.template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
-
m.template 'fixtures.yml', File.join('test/fixtures', class_path, "#{table_name}.yml")
-
-
unless options[:skip_migration]
-
m.migration_template 'migration.rb', 'db/migrate', :assigns => {
-
:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
-
}, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
-
end
-
end
-
end
-
-
protected
-
def add_options!(opt)
-
opt.separator ''
-
opt.separator 'Options:'
-
opt.on("--skip-migration",
-
"Don't generate a migration file for this model") { |v| options[:skip_migration] = v }
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) <%= '2015'; DateTime.now.year %> Genome Research Ltd.
-
class <%= migration_name %> < ActiveRecord::Migration
-
-
def self.up
-
create_table :<%= (table_name < 'users') ? "#{table_name}_users" : "users_#{table_name}" %>, :id => false, :force => true do |t|
-
t.integer :user_id, :<%= singular_name %>_id
-
t.timestamps
-
end
-
-
create_table :<%= table_name %>, :force => true do |t|
-
t.string :name, :authorizable_type, :limit => 40
-
t.integer :authorizable_id
-
t.timestamps
-
end
-
end
-
-
def self.down
-
drop_table :<%= table_name %>
-
drop_table :<%= (table_name < 'users') ? "#{table_name}_users" : "users_#{table_name}" %>
-
end
-
-
end
-
# Defines named roles for users that may be applied to
-
# objects in a polymorphic fashion. For example, you could create a role
-
# "moderator" for an instance of a model (i.e., an object), a model class,
-
# or without any specification at all.
-
class <%= class_name %> < ActiveRecord::Base
-
has_and_belongs_to_many :users
-
belongs_to :authorizable, :polymorphic => true
-
end
-
require File.dirname(__FILE__) + '<%= '/..' * class_nesting_depth %>/../test_helper'
-
-
class <%= class_name %>Test < Test::Unit::TestCase
-
fixtures :<%= table_name %>
-
-
# Replace this with your real tests.
-
def test_truth
-
assert true
-
end
-
end
-
puts IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
-
-
1
require File.dirname(__FILE__) + '/publishare/exceptions'
-
1
require File.dirname(__FILE__) + '/publishare/parser'
-
-
1
module Authorization
-
1
module Base
-
-
# Modify these constants in your environment.rb to tailor the plugin to
-
# your authentication system
-
1
if not Object.constants.include? "LOGIN_REQUIRED_REDIRECTION"
-
1
LOGIN_REQUIRED_REDIRECTION = {
-
:controller => 'session',
-
:action => 'new'
-
}
-
end
-
1
if not Object.constants.include? "PERMISSION_DENIED_REDIRECTION"
-
1
PERMISSION_DENIED_REDIRECTION = ''
-
end
-
1
if not Object.constants.include? "STORE_LOCATION_METHOD"
-
1
STORE_LOCATION_METHOD = :store_location
-
end
-
-
1
def self.included( recipient )
-
1
recipient.extend( ControllerClassMethods )
-
1
recipient.class_eval do
-
1
include ControllerInstanceMethods
-
end
-
end
-
-
1
module ControllerClassMethods
-
-
# Allow class-level authorization check.
-
# permit is used in a before_filter fashion and passes arguments to the before_filter.
-
1
def permit( authorization_expression, *args )
-
filter_keys = [ :only, :except ]
-
filter_args, eval_args = {}, {}
-
if args.last.is_a? Hash
-
filter_args.merge!( args.last.reject {|k,v| not filter_keys.include? k } )
-
eval_args.merge!( args.last.reject {|k,v| filter_keys.include? k } )
-
end
-
before_filter( filter_args ) do |controller|
-
controller.permit( authorization_expression, eval_args )
-
end
-
end
-
end
-
-
1
module ControllerInstanceMethods
-
1
include Authorization::Base::EvalParser # RecursiveDescentParser is another option
-
-
# Permit? turns off redirection by default and takes no blocks
-
1
def permit?( authorization_expression, *args )
-
@options = { :allow_guests => false, :redirect => false }
-
@options.merge!( args.last.is_a?( Hash ) ? args.last : {} )
-
-
has_permission?( authorization_expression )
-
end
-
-
# Allow method-level authorization checks.
-
# permit (without a question mark ending) calls redirect on denial by default.
-
# Specify :redirect => false to turn off redirection.
-
1
def permit( authorization_expression, *args )
-
@options = { :allow_guests => false, :redirect => true }
-
@options.merge!( args.last.is_a?( Hash ) ? args.last : {} )
-
-
if has_permission?( authorization_expression )
-
yield if block_given?
-
elsif @options[:redirect]
-
handle_redirection
-
end
-
end
-
-
1
private
-
-
1
def has_permission?( authorization_expression )
-
@current_user = get_user
-
if not @options[:allow_guests]
-
# We aren't logged in, or an exception has already been raised.
-
# Test for both nil and :false symbol as restful_authentication plugin
-
# will set current user to ':false' on a failed login (patch by Ho-Sheng Hsiao).
-
# Latest incarnations of restful_authentication plugin set current user to false.
-
if @current_user.nil? || @current_user == :false || @current_user == false
-
return false
-
elsif not @current_user.respond_to? :id
-
raise( UserDoesntImplementID, "User doesn't implement #id")
-
elsif not @current_user.respond_to? :has_role?
-
raise( UserDoesntImplementRoles, "User doesn't implement #has_role?" )
-
end
-
end
-
parse_authorization_expression( authorization_expression )
-
end
-
-
# Handle redirection within permit if authorization is denied.
-
1
def handle_redirection
-
return if not self.respond_to?( :redirect_to )
-
-
# Store url in session for return if this is available from
-
# authentication
-
send( STORE_LOCATION_METHOD ) if respond_to? STORE_LOCATION_METHOD
-
if @current_user && @current_user != :false
-
flash[:notice] = @options[:permission_denied_message] || "Permission denied. You cannot access the requested page."
-
redirect_to @options[:permission_denied_redirection] || PERMISSION_DENIED_REDIRECTION
-
else
-
flash[:notice] = @options[:login_required_message] || "Login is required to access the requested page."
-
redirect_to @options[:login_required_redirection] || LOGIN_REQUIRED_REDIRECTION
-
end
-
false # Want to short-circuit the filters
-
end
-
-
# Try to find current user by checking options hash and instance method in that order.
-
1
def get_user
-
if @options[:user]
-
@options[:user]
-
elsif @options[:get_user_method]
-
send( @options[:get_user_method] )
-
elsif self.respond_to? :current_user
-
current_user
-
elsif not @options[:allow_guests]
-
raise( CannotObtainUserObject, "Couldn't find #current_user or @user, and nothing appropriate found in hash" )
-
end
-
end
-
-
# Try to find a model to query for permissions
-
1
def get_model( str )
-
if str =~ /\s*([A-Z]+\w*)\s*/
-
# Handle model class
-
begin
-
Module.const_get( str )
-
rescue
-
raise CannotObtainModelClass, "Couldn't find model class: #{str}"
-
end
-
elsif str =~ /\s*:*(\w+)\s*/
-
# Handle model instances
-
model_name = $1
-
model_symbol = model_name.to_sym
-
if @options[model_symbol]
-
@options[model_symbol]
-
elsif instance_variables.include?( '@'+model_name )
-
instance_variable_get( '@'+model_name )
-
# Note -- while the following code makes autodiscovery more convenient, it's a little too much side effect & security question
-
# elsif self.params[:id]
-
# eval_str = model_name.camelize + ".find(#{self.params[:id]})"
-
# eval eval_str
-
else
-
raise CannotObtainModelObject, "Couldn't find model (#{str}) in hash or as an instance variable"
-
end
-
end
-
end
-
end
-
-
end
-
end
-
1
module Authorization #:nodoc:
-
-
# Base error class for Authorization module
-
1
class AuthorizationError < StandardError
-
end
-
-
# Raised when the authorization expression is invalid (cannot be parsed)
-
1
class AuthorizationExpressionInvalid < AuthorizationError
-
end
-
-
# Raised when we can't find the current user
-
1
class CannotObtainUserObject < AuthorizationError
-
end
-
-
# Raised when an authorization expression contains a model class that doesn't exist
-
1
class CannotObtainModelClass < AuthorizationError
-
end
-
-
# Raised when an authorization expression contains a model reference that doesn't exist
-
1
class CannotObtainModelObject < AuthorizationError
-
end
-
-
# Raised when the obtained user object doesn't implement #id
-
1
class UserDoesntImplementID < AuthorizationError
-
end
-
-
# Raised when the obtained user object doesn't implement #has_role?
-
1
class UserDoesntImplementRoles < AuthorizationError
-
end
-
-
# Raised when the obtained model doesn't implement #accepts_role?
-
1
class ModelDoesntImplementRoles < AuthorizationError
-
end
-
-
1
class CannotSetRoleWhenHardwired < AuthorizationError
-
end
-
-
1
class CannotSetObjectRoleWhenSimpleRoleTable < AuthorizationError
-
end
-
-
1
class CannotGetAuthorizables < AuthorizationError
-
end
-
end
-
require File.dirname(__FILE__) + '/exceptions'
-
-
# In order to use this mixin, you'll need to define roles by overriding the
-
# following functions:
-
#
-
# User#has_role?(role)
-
# Return true or false depending on the roles (strings) passed in.
-
#
-
# Model#accepts_role?(role, user)
-
# Return true or false depending on the roles (strings) this particular user has for
-
# this particular model object.
-
#
-
# See http://www.writertopia.com/developers/authorization
-
-
module Authorization
-
module HardwiredRoles
-
-
module UserExtensions
-
def self.included( recipient )
-
recipient.extend( ClassMethods )
-
end
-
-
module ClassMethods
-
def acts_as_authorized_user
-
include Authorization::HardwiredRoles::UserExtensions::InstanceMethods
-
end
-
end
-
-
module InstanceMethods
-
# If roles aren't explicitly defined in user class then return false
-
def has_role?( role, authorizable_object = nil )
-
false
-
end
-
-
def has_role( role, authorizable_object = nil )
-
raise( CannotSetRoleWhenHardwired,
-
"Hardwired mixin: Cannot set user to role #{role}. Don't use #has_role, use code in models."
-
)
-
end
-
-
def has_no_role( role, authorizable_object = nil )
-
raise( CannotSetRoleWhenHardwired,
-
"Hardwired mixin: Cannot remove user role #{role}. Don't use #has_no_role, use code in models."
-
)
-
end
-
end
-
end
-
-
module ModelExtensions
-
def self.included( recipient )
-
recipient.extend( ClassMethods )
-
end
-
-
module ClassMethods
-
def acts_as_authorizable
-
include Authorization::HardwiredRoles::ModelExtensions::InstanceMethods
-
end
-
end
-
-
module InstanceMethods
-
def accepts_role?( role, user )
-
return false
-
end
-
-
def accepts_role( role, user )
-
raise( CannotSetRoleWhenHardwired,
-
"Hardwired mixin: Cannot set user to role #{role}. Don't use #accepts_role, use code in models."
-
)
-
end
-
-
def accepts_no_role( role, user )
-
raise( CannotSetRoleWhenHardwired,
-
"Hardwired mixin: Cannot set user to role #{role}. Don't use #accepts_no_role, use code in models."
-
)
-
end
-
end
-
end
-
-
end
-
-
end
-
-
1
require File.dirname(__FILE__) + '/exceptions'
-
-
# Provides the appearance of dynamically generated methods on the roles database.
-
#
-
# Examples:
-
# user.is_member? --> Returns true if user has any role of "member"
-
# user.is_member_of? this_workshop --> Returns true/false. Must have authorizable object after query.
-
# user.is_eligible_for [this_award] --> Gives user the role "eligible" for "this_award"
-
# user.is_moderator --> Gives user the general role "moderator" (not tied to any class or object)
-
# user.is_candidate_of_what --> Returns array of objects for which this user is a "candidate" (any type)
-
# user.is_candidate_of_what(Party) --> Returns array of objects for which this user is a "candidate" (only 'Party' type)
-
#
-
# model.has_members --> Returns array of users which have role "member" on that model
-
# model.has_members? --> Returns true/false
-
#
-
1
module Authorization
-
1
module Identity
-
-
1
module UserExtensions
-
1
module InstanceMethods
-
-
1
def method_missing( method_sym, *args )
-
1
method_name = method_sym.to_s
-
1
authorizable_object = args.empty? ? nil : args[0]
-
-
1
base_regex = "is_(\\w+)"
-
1
fancy_regex = base_regex + "_(#{Authorization::Base::VALID_PREPOSITIONS_PATTERN})"
-
1
is_either_regex = '^((' + fancy_regex + ')|(' + base_regex + '))'
-
1
base_not_regex = "is_no[t]?_(\\w+)"
-
1
fancy_not_regex = base_not_regex + "_(#{Authorization::Base::VALID_PREPOSITIONS_PATTERN})"
-
1
is_not_either_regex = '^((' + fancy_not_regex + ')|(' + base_not_regex + '))'
-
-
1
if method_name =~ Regexp.new(is_either_regex + '_what$')
-
role_name = $3 || $6
-
has_role_for_objects(role_name, authorizable_object)
-
1
elsif method_name =~ Regexp.new(is_not_either_regex + '\?$')
-
role_name = $3 || $6
-
not is_role?( role_name, authorizable_object )
-
1
elsif method_name =~ Regexp.new(is_either_regex + '\?$')
-
1
role_name = $3 || $6
-
1
is_role?( role_name, authorizable_object )
-
elsif method_name =~ Regexp.new(is_not_either_regex + '$')
-
role_name = $3 || $6
-
is_no_role( role_name, authorizable_object )
-
elsif method_name =~ Regexp.new(is_either_regex + '$')
-
role_name = $3 || $6
-
is_role( role_name, authorizable_object )
-
else
-
super
-
end
-
end
-
-
1
private
-
-
1
def is_role?( role_name, authorizable_object )
-
1
if authorizable_object.nil?
-
1
return self.has_role?(role_name)
-
elsif authorizable_object.respond_to?(:accepts_role?)
-
return self.has_role?(role_name, authorizable_object)
-
end
-
false
-
end
-
-
1
def is_no_role( role_name, authorizable_object = nil )
-
if authorizable_object.nil?
-
self.has_no_role role_name
-
else
-
self.has_no_role role_name, authorizable_object
-
end
-
end
-
-
1
def is_role( role_name, authorizable_object = nil )
-
if authorizable_object.nil?
-
self.has_role role_name
-
else
-
self.has_role role_name, authorizable_object
-
end
-
end
-
-
1
def has_role_for_objects(role_name, type)
-
if type.nil?
-
roles = self.roles.find_all_by_name( role_name )
-
else
-
roles = self.roles.find_all_by_authorizable_type_and_name( type.name, role_name )
-
end
-
roles.collect do |role|
-
if role.authorizable_id.nil?
-
role.authorizable_type.nil? ?
-
nil : Module.const_get( role.authorizable_type ) # Returns class
-
else
-
role.authorizable
-
end
-
end
-
end
-
end
-
end
-
-
1
module ModelExtensions
-
1
module InstanceMethods
-
-
1
def method_missing( method_sym, *args )
-
method_name = method_sym.to_s
-
if method_name =~ /^has_(\w+)\?$/
-
roles = $1.split('_or_').collect { |role| role.singularize }
-
roles = roles.flatten.compact
-
self.accepted_roles.find_all_by_name(roles, :include => :users).any? { |role| role.users.compact.any? }
-
elsif method_name =~ /^has_(\w+)$/
-
roles = $1.split('_or_').collect { |role| role.singularize }
-
roles = roles.flatten.compact
-
users = self.accepted_roles.find_all_by_name(roles, :include => :users).collect { |role| role.users }
-
users.flatten.compact.uniq if users
-
else
-
super
-
end
-
end
-
-
end
-
end
-
-
end
-
end
-
1
require File.dirname(__FILE__) + '/exceptions'
-
1
require File.dirname(__FILE__) + '/identity'
-
-
1
module Authorization
-
1
module ObjectRolesTable
-
-
1
module UserExtensions
-
1
def self.included( recipient )
-
1
recipient.extend( ClassMethods )
-
end
-
-
1
module ClassMethods
-
1
def acts_as_authorized_user(roles_relationship_opts = {})
-
1
has_many :user_role_bindings, :class_name => 'Role::UserRole'
-
1
has_many :roles, roles_relationship_opts.merge(:through => :user_role_bindings, :source => :role)
-
# has_and_belongs_to_many :roles, roles_relationship_opts
-
1
include Authorization::ObjectRolesTable::UserExtensions::InstanceMethods
-
1
include Authorization::Identity::UserExtensions::InstanceMethods # Provides all kinds of dynamic sugar via method_missing
-
end
-
end
-
-
1
module InstanceMethods
-
# If roles aren't explicitly defined in user class then check roles table
-
1
def has_role?( role_name, authorizable_obj = nil )
-
1
if authorizable_obj.nil?
-
1
self.roles.find_by_name( role_name ) || self.roles.member?(get_role( role_name, authorizable_obj )) ? true : false # If we ask a general role question, return true if any role is defined.
-
else
-
role = get_role( role_name, authorizable_obj )
-
role ? self.roles.exists?( role.id ) : false
-
end
-
end
-
-
1
def has_role( role_name, authorizable_obj = nil )
-
role = get_role( role_name, authorizable_obj )
-
if role.nil?
-
if authorizable_obj.is_a? Class
-
role = Role.create( :name => role_name, :authorizable_type => authorizable_obj.to_s )
-
elsif authorizable_obj
-
role = Role.create( :name => role_name, :authorizable => authorizable_obj )
-
else
-
role = Role.create( :name => role_name )
-
end
-
end
-
self.roles << role if role and not self.roles.exists?( role.id )
-
end
-
-
1
def has_no_role( role_name, authorizable_obj = nil )
-
role = get_role( role_name, authorizable_obj )
-
delete_role( role )
-
end
-
-
1
def has_roles_for?( authorizable_obj )
-
if authorizable_obj.is_a? Class
-
!self.roles.detect { |role| role.authorizable_type == authorizable_obj.to_s }.nil?
-
elsif authorizable_obj
-
!self.roles.detect { |role| role.authorizable_type == authorizable_obj.class.base_class.to_s && role.authorizable == authorizable_obj }.nil?
-
else
-
!self.roles.detect { |role| role.authorizable.nil? }.nil?
-
end
-
end
-
1
alias :has_role_for? :has_roles_for?
-
-
1
def roles_for( authorizable_obj )
-
if authorizable_obj.is_a? Class
-
self.roles.select { |role| role.authorizable_type == authorizable_obj.to_s }
-
elsif authorizable_obj
-
self.roles.select { |role| role.authorizable_type == authorizable_obj.class.base_class.to_s && role.authorizable.id == authorizable_obj.id }
-
else
-
self.roles.select { |role| role.authorizable.nil? }
-
end
-
end
-
-
1
def has_no_roles_for(authorizable_obj = nil)
-
roles_for(authorizable_obj).each { |role| delete_role( role ) }
-
end
-
-
1
def has_no_roles
-
self.roles.each { |role| delete_role( role ) }
-
end
-
-
1
def authorizables_for( authorizable_class )
-
unless authorizable_class.is_a? Class
-
raise CannotGetAuthorizables, "Invalid argument: '#{authorizable_class}'. You must provide a class here."
-
end
-
begin
-
authorizable_class.find(
-
self.roles.find_all_by_authorizable_type(authorizable_class.base_class.to_s).map(&:authorizable_id).uniq
-
)
-
rescue ActiveRecord::RecordNotFound
-
[]
-
end
-
end
-
-
1
private
-
-
1
def get_role( role_name, authorizable_obj )
-
1
if authorizable_obj.is_a? Class
-
Role.find( :first,
-
:conditions => [ 'name = ? and authorizable_type = ? and authorizable_id IS NULL', role_name, authorizable_obj.to_s ] )
-
1
elsif authorizable_obj
-
Role.find( :first,
-
:conditions => [ 'name = ? and authorizable_type = ? and authorizable_id = ?',
-
role_name, authorizable_obj.class.base_class.to_s, authorizable_obj.id ] )
-
else
-
1
Role.find( :first,
-
:conditions => [ 'name = ? and authorizable_type IS NULL and authorizable_id IS NULL', role_name ] )
-
end
-
end
-
-
1
def delete_role( role )
-
if role
-
self.roles.delete( role )
-
role.destroy if role.users.empty?
-
end
-
end
-
-
end
-
end
-
-
1
module ModelExtensions
-
1
def self.included( recipient )
-
1
recipient.extend( ClassMethods )
-
end
-
-
1
module ClassMethods
-
1
def acts_as_authorizable
-
2
has_many :accepted_roles, :as => :authorizable, :class_name => 'Role', :dependent => :destroy
-
-
2
has_many :users, :finder_sql => 'SELECT DISTINCT users.* FROM users INNER JOIN roles_users ON user_id = users.id INNER JOIN roles ON roles.id = role_id WHERE authorizable_type = \'#{self.class.base_class.to_s}\' AND authorizable_id = #{id}', :counter_sql => 'SELECT COUNT(DISTINCT users.id) FROM users INNER JOIN roles_users ON user_id = users.id INNER JOIN roles ON roles.id = role_id WHERE authorizable_type = \'#{self.class.base_class.to_s}\' AND authorizable_id = #{id}', :readonly => true
-
-
2
def accepts_role?( role_name, user )
-
user.has_role? role_name, self
-
end
-
-
2
def accepts_role( role_name, user )
-
user.has_role role_name, self
-
end
-
-
2
def accepts_no_role( role_name, user )
-
user.has_no_role role_name, self
-
end
-
-
2
def accepts_roles_by?( user )
-
user.has_roles_for? self
-
end
-
2
alias :accepts_role_by? :accepts_roles_by?
-
-
2
def accepted_roles_by( user )
-
user.roles_for self
-
end
-
-
2
def authorizables_by( user )
-
user.authorizables_for self
-
end
-
-
2
include Authorization::ObjectRolesTable::ModelExtensions::InstanceMethods
-
2
include Authorization::Identity::ModelExtensions::InstanceMethods # Provides all kinds of dynamic sugar via method_missing
-
end
-
end
-
-
1
module InstanceMethods
-
# If roles aren't overriden in model then check roles table
-
1
def accepts_role?( role_name, user )
-
user.has_role? role_name, self
-
end
-
-
1
def accepts_role( role_name, user )
-
user.has_role role_name, self
-
end
-
-
1
def accepts_no_role( role_name, user )
-
user.has_no_role role_name, self
-
end
-
-
1
def accepts_roles_by?( user )
-
user.has_roles_for? self
-
end
-
1
alias :accepts_role_by? :accepts_roles_by?
-
-
1
def accepted_roles_by( user )
-
user.roles_for self
-
end
-
-
end
-
end
-
-
end
-
end
-
-
1
module Authorization
-
1
module Base
-
-
1
VALID_PREPOSITIONS = ['of', 'for', 'in', 'on', 'to', 'at', 'by']
-
1
BOOLEAN_OPS = ['not', 'or', 'and']
-
1
VALID_PREPOSITIONS_PATTERN = VALID_PREPOSITIONS.join('|')
-
-
1
module EvalParser
-
# Parses and evaluates an authorization expression and returns <tt>true</tt> or <tt>false</tt>.
-
#
-
# The authorization expression is defined by the following grammar:
-
# <expr> ::= (<expr>) | not <expr> | <term> or <expr> | <term> and <expr> | <term>
-
# <term> ::= <role> | <role> <preposition> <model>
-
# <preposition> ::= of | for | in | on | to | at | by
-
# <model> ::= /:*\w+/
-
# <role> ::= /\w+/ | /'.*'/
-
#
-
# Instead of doing recursive descent parsing (not so fun when we support nested parentheses, etc),
-
# we let Ruby do the work for us by inserting the appropriate permission calls and using eval.
-
# This would not be a good idea if you were getting authorization expressions from the outside,
-
# so in that case (e.g. somehow letting users literally type in permission expressions) you'd
-
# be better off using the recursive descent parser in Module RecursiveDescentParser.
-
#
-
# We search for parts of our authorization evaluation that match <role> or <role> <preposition> <model>
-
# and we ignore anything terminal in our grammar.
-
#
-
# 1) Replace all <role> <preposition> <model> matches.
-
# 2) Replace all <role> matches that aren't one of our other terminals ('not', 'or', 'and', or preposition)
-
# 3) Eval
-
-
1
def parse_authorization_expression( str )
-
if str =~ /[^A-Za-z0-9_:'\(\)\s]/
-
raise AuthorizationExpressionInvalid, "Invalid authorization expression (#{str})"
-
return false
-
end
-
@replacements = []
-
expr = replace_temporarily_role_of_model( str )
-
expr = replace_role( expr )
-
expr = replace_role_of_model( expr )
-
begin
-
instance_eval( expr )
-
rescue
-
raise AuthorizationExpressionInvalid, "Cannot parse authorization (#{str})"
-
end
-
end
-
-
1
def replace_temporarily_role_of_model( str )
-
role_regex = '\s*(\'\s*(.+?)\s*\'|(\w+))\s+'
-
model_regex = '\s+(:*\w+)'
-
parse_regex = Regexp.new(role_regex + '(' + VALID_PREPOSITIONS.join('|') + ')' + model_regex)
-
str.gsub(parse_regex) do |match|
-
@replacements.push " process_role_of_model('#{$2 || $3}', '#{$5}') "
-
" <#{@replacements.length-1}> "
-
end
-
end
-
-
1
def replace_role( str )
-
role_regex = '\s*(\'\s*(.+?)\s*\'|([A-Za-z]\w*))\s*'
-
parse_regex = Regexp.new(role_regex)
-
str.gsub(parse_regex) do |match|
-
if BOOLEAN_OPS.include?($3)
-
" #{match} "
-
else
-
" process_role('#{$2 || $3}') "
-
end
-
end
-
end
-
-
1
def replace_role_of_model( str )
-
str.gsub(/<(\d+)>/) do |match|
-
@replacements[$1.to_i]
-
end
-
end
-
-
1
def process_role_of_model( role_name, model_name )
-
model = get_model( model_name )
-
raise( ModelDoesntImplementRoles, "Model (#{model_name}) doesn't implement #accepts_role?" ) if not model.respond_to? :accepts_role?
-
model.send( :accepts_role?, role_name, @current_user )
-
end
-
-
1
def process_role( role_name )
-
return false if @current_user.nil? || @current_user == :false
-
raise( UserDoesntImplementRoles, "User doesn't implement #has_role?" ) if not @current_user.respond_to? :has_role?
-
@current_user.has_role?( role_name )
-
end
-
-
end
-
-
# Parses and evaluates an authorization expression and returns <tt>true</tt> or <tt>false</tt>.
-
# This recursive descent parses uses two instance variables:
-
# @stack --> a stack with the top holding the boolean expression resulting from the parsing
-
#
-
# The authorization expression is defined by the following grammar:
-
# <expr> ::= (<expr>) | not <expr> | <term> or <expr> | <term> and <expr> | <term>
-
# <term> ::= <role> | <role> <preposition> <model>
-
# <preposition> ::= of | for | in | on | to | at | by
-
# <model> ::= /:*\w+/
-
# <role> ::= /\w+/ | /'.*'/
-
#
-
# There are really two values we must track:
-
# (1) whether the expression is valid according to the grammar
-
# (2) the evaluated results --> true/false on the permission queries
-
# The first is embedded in the control logic because we want short-circuiting. If an expression
-
# has been parsed and the permission is false, we don't want to try different ways of parsing.
-
# Note that this implementation of a recursive descent parser is meant to be simple
-
# and doesn't allow arbitrary nesting of parentheses. It supports up to 5 levels of nesting.
-
# It also won't handle some types of expressions (A or B) and C, which has to be rewritten as
-
# C and (A or B) so the parenthetical expressions are in the tail.
-
1
module RecursiveDescentParser
-
-
1
OPT_PARENTHESES_PATTERN = '(([^()]|\(([^()]|\(([^()]|\(([^()]|\(([^()]|\(([^()])*\))*\))*\))*\))*\))*)'
-
1
PARENTHESES_PATTERN = '\(' + OPT_PARENTHESES_PATTERN + '\)'
-
1
NOT_PATTERN = '^\s*not\s+' + OPT_PARENTHESES_PATTERN + '$'
-
1
AND_PATTERN = '^\s*' + OPT_PARENTHESES_PATTERN + '\s+and\s+' + OPT_PARENTHESES_PATTERN + '\s*$'
-
1
OR_PATTERN = '^\s*' + OPT_PARENTHESES_PATTERN + '\s+or\s+' + OPT_PARENTHESES_PATTERN + '\s*$'
-
1
ROLE_PATTERN = '(\'\s*(.+)\s*\'|(\w+))'
-
1
MODEL_PATTERN = '(:*\w+)'
-
-
1
PARENTHESES_REGEX = Regexp.new('^\s*' + PARENTHESES_PATTERN + '\s*$')
-
1
NOT_REGEX = Regexp.new(NOT_PATTERN)
-
1
AND_REGEX = Regexp.new(AND_PATTERN)
-
1
OR_REGEX = Regexp.new(OR_PATTERN)
-
1
ROLE_REGEX = Regexp.new('^\s*' + ROLE_PATTERN + '\s*$')
-
1
ROLE_OF_MODEL_REGEX = Regexp.new('^\s*' + ROLE_PATTERN + '\s+(' + VALID_PREPOSITIONS_PATTERN + ')\s+' + MODEL_PATTERN + '\s*$')
-
-
1
def parse_authorization_expression( str )
-
@stack = []
-
raise AuthorizationExpressionInvalid, "Cannot parse authorization (#{str})" if not parse_expr( str )
-
return @stack.pop
-
end
-
-
1
def parse_expr( str )
-
parse_parenthesis( str ) or
-
parse_not( str ) or
-
parse_or( str ) or
-
parse_and( str ) or
-
parse_term( str )
-
end
-
-
1
def parse_not( str )
-
if str =~ NOT_REGEX
-
can_parse = parse_expr( $1 )
-
@stack.push( !@stack.pop ) if can_parse
-
end
-
false
-
end
-
-
1
def parse_or( str )
-
if str =~ OR_REGEX
-
can_parse = parse_expr( $1 ) and parse_expr( $8 )
-
@stack.push( @stack.pop | @stack.pop ) if can_parse
-
return can_parse
-
end
-
false
-
end
-
-
1
def parse_and( str )
-
if str =~ AND_REGEX
-
can_parse = parse_expr( $1 ) and parse_expr( $8 )
-
@stack.push(@stack.pop & @stack.pop) if can_parse
-
return can_parse
-
end
-
false
-
end
-
-
# Descend down parenthesis (allow up to 5 levels of nesting)
-
1
def parse_parenthesis( str )
-
str =~ PARENTHESES_REGEX ? parse_expr( $1 ) : false
-
end
-
-
1
def parse_term( str )
-
parse_role_of_model( str ) or
-
parse_role( str )
-
end
-
-
# Parse <role> of <model>
-
1
def parse_role_of_model( str )
-
if str =~ ROLE_OF_MODEL_REGEX
-
role_name = $2 || $3
-
model_name = $5
-
model_obj = get_model( model_name )
-
raise( ModelDoesntImplementRoles, "Model (#{model_name}) doesn't implement #accepts_role?" ) if not model_obj.respond_to? :accepts_role?
-
-
has_permission = model_obj.send( :accepts_role?, role_name, @current_user )
-
@stack.push( has_permission )
-
true
-
else
-
false
-
end
-
end
-
-
# Parse <role> of the User-like object
-
1
def parse_role( str )
-
if str =~ ROLE_REGEX
-
role_name = $1
-
if @current_user.nil? || @current_user == :false
-
@stack.push(false)
-
else
-
raise( UserDoesntImplementRoles, "User doesn't implement #has_role?" ) if not @current_user.respond_to? :has_role?
-
@stack.push( @current_user.has_role?(role_name) )
-
end
-
true
-
else
-
false
-
end
-
end
-
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module RequestTypePurposeCreation
-
-
def add_request_purpose
-
purpose_key = self.request_class <= TransferRequest ? 'internal' : 'standard'
-
self.request_purpose ||= RequestPurpose.find_by_key!(purpose_key)
-
self
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
# This module may be used to benchmark the API processes
-
-
class ResponseTimer
-
require 'java'
-
-
class InstanceTimer
-
def initialize(start,output,response,env)
-
@start = start
-
@output = output
-
@response = response
-
@env = env
-
end
-
-
def close
-
@response.close if @response.respond_to?(:close) # Pass us on
-
stop = Time.now
-
@output.syswrite "Request: #{@env['REQUEST_METHOD']}%#{@env['REQUEST_URI']}%#{@env["rack.input"].string}%#{stop-@start}\n"
-
#@output.syswrite %Q{curl -X #{@env['REQUEST_METHOD']} -H "X_SEQUENCESCAPE_CLIENT_ID:development" -H "USER_AGENT:Ruby" -H "COOKIE:WTSISignOn=" -H "ACCEPT:application/json" -H "Content-Type: application/json" -d '#{@env["rack.input"].string}' http://localhost:3000#{@env['REQUEST_PATH']} --noproxy localhost\n}
-
end
-
-
def each(&block)
-
@response.each(&block)
-
end
-
end
-
-
def initialize(app,output)
-
@app = app
-
@output = output
-
header
-
end
-
-
def call(env)
-
start = Time.now
-
status, headers, response = @app.call(env)
-
[status, headers, InstanceTimer.new(start,@output,response,env)]
-
end
-
-
def header
-
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'mri'
-
@output.syswrite <<-HEADER
-
Rails response log
-
Started at: #{Time.now}
-
Environment: #{Rails.env}:R#{RUBY_VERSION}:#{File.split(Rails.root).last.capitalize}:#{engine}
-
------------
-
HEADER
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
# Place to put Illumina QC code to be refactored
-
1
module SequencingQcBatch
-
# NOTE: Be careful that the length of these do not exceed 25 characters, otherwise you will have to alter the
-
# batches.qc_state field in the DB to accommodate. FYI, 25 characters is:
-
# <----------------------->
-
1
VALID_QC_STATES = [
-
"qc_pending",
-
"qc_submitted",
-
"qc_manual",
-
"qc_manual_in_progress",
-
"qc_completed"
-
]
-
-
1
def self.included(base)
-
1
base.instance_eval do
-
1
extend ClassMethods
-
-
# TODO[xxx]: Isn't qc_state supposed to be initialised to 'qc_pending' rather than blank?
-
1
validates_inclusion_of :qc_state, :in => VALID_QC_STATES, :allow_blank => true
-
-
1
belongs_to :qc_pipeline, :class_name => "Pipeline"
-
1
before_create :qc_pipeline_update
-
end
-
end
-
-
1
module ClassMethods
-
# Based on the structure of the document found in test/data/activeMQ_message_example.xml
-
# {"evaluations"=>{"evaluation"=>{"result"=>nil, "checks"=>{"check"=>{"results"=>"Some free form data (no html please)", "criteria"=>{"criterion"=>[{"value"=>"Greater than 80mb", "key"=>"yield"}, {"value"=>"Greater than Q20", "key"=>"count"}]}, "data_source"=>"/somewhere.fastq", "links"=>{"link"=>{"href"=>"http://example.com/some_interesting_image_or_table", "label"=>"display text for hyperlink"}}, "comment"=>"All good", "pass"=>"true"}}, "check"=>"Auto QC", "identifier"=>"batch id", "location"=>"lane number"}}}
-
1
def qc_evaluations_update(evaluations)
-
if evaluations.has_key?("evaluation")
-
evaluations.each do |key, evaluation|
-
if evaluation.kind_of?(Array)
-
evaluation.each do |ev|
-
Batch.qc_assets_update(ev)
-
end
-
elsif evaluation.kind_of?(Hash)
-
Batch.qc_assets_update(evaluation)
-
end
-
end
-
end
-
end
-
-
1
def qc_assets_update(evaluation)
-
b = Batch.find(evaluation["identifier"], :include => [:requests])
-
# Checks if batch.qc_state is not qc_manual, then change it
-
# so that it appears in the Manual QC pipeline
-
if b.qc_state == "qc_pending" || b.qc_state == "qc_submitted"
-
b.qc_ready_for_manual
-
end
-
-
-
br = b.batch_requests.first(:conditions => {:position => evaluation["location"]})
-
-
result = evaluation["result"]
-
unless result.nil? || br.request.target_asset.nil? # nil e.g. controls without source/target asset
-
br.request.target_asset.qc_state = result
-
br.save
-
b.lab_events.create(:description => "Auto QC result", :message => result.humanize)
-
end
-
evaluation["checks"].each_value do |v|
-
v = [v] unless v.kind_of?(Array)
-
v.each do |value|
-
br.request.lab_events.create(:description => evaluation["check"], :descriptors => value, :descriptor_fields => value.keys, :batch_id => b.id)
-
end
-
end
-
br.save
-
end
-
end
-
-
#--
-
# Batches have, in addition to the State Machine "state", two additional states: qc_state and production_state
-
# qc_state is used to track QC process in pipelines and when the QC process is triggered from NPG and when it ends
-
# qc_production allows a whole batch, and its items, to fail or pass regardless of the QC state.
-
# The last State Machine state that a batch can reach is "released"
-
# A batch cannot be started once it fails or released
-
# QC State ["qc_pending", "qc_manual", "qc_manual_in_progress","qc_completed"]
-
#++
-
-
# View based check to display batches with results
-
1
def show_in_manual_qc?
-
if assets_qc_tasks_results.uniq.size > 1
-
answer = false
-
else
-
answer = true
-
end
-
answer
-
end
-
-
# Cron job specific checker
-
1
def migrate_to_manual_qc?
-
if assets_qc_tasks_results.include?(true)
-
answer = true
-
else
-
answer = false
-
end
-
answer
-
end
-
-
# Returns qc_states used
-
1
def qc_states
-
VALID_QC_STATES
-
end
-
-
1
def qc_previous_state!(current_user)
-
previous_state = self.qc_previous_state
-
if previous_state
-
self.lab_events.create(:description => "QC Rollback", :message => "Manual QC moved from #{self.qc_state} to #{previous_state}", :user_id => current_user.id)
-
self.qc_state = previous_state
-
end
-
self.state = 'started'
-
self.save
-
end
-
-
1
def self.adjacent_state_helper(direction, offset, delimiter)
-
2
define_method(:"qc_#{ direction }_state") do
-
raise StandardError, "Current QC state appears to be invalid: '#{ self.qc_state }'" unless qc_states.include?(self.qc_state.to_s)
-
return nil if self.qc_state.to_s == qc_states.send(delimiter)
-
return qc_states[ qc_states.index(self.qc_state.to_s)+offset ]
-
end
-
end
-
-
# Sets up qc_next_state and qc_previous_state so that they move in the appropriate direction to find their
-
# appropriate state, and are limited by the last or first states (respectively).
-
1
adjacent_state_helper(:next, +1, :last)
-
1
adjacent_state_helper(:previous, -1, :first)
-
-
1
def self.state_transition_helper(name)
-
# TODO[xxx]: Really we should restrict the state transitions
-
3
define_method(:"qc_#{ name }") do
-
self.update_attribute(:qc_state, self.qc_next_state) unless self.qc_next_state.nil?
-
end
-
end
-
-
# Define the various state transition helpers ...
-
1
state_transition_helper(:submitted)
-
1
state_transition_helper(:criteria_received)
-
1
state_transition_helper(:complete)
-
-
1
def processing_in_manual_qc?
-
[ 'qc_manual_in_progress', 'qc_manual' ].include?(self.qc_state)
-
end
-
-
1
def qc_pipeline_workflow_id
-
pipeline = Pipeline.first(:conditions => {:name => "quality control", :automated => true})
-
pipeline.workflow.id
-
end
-
-
1
def qc_ready_for_manual
-
ActiveRecord::Base.transaction do
-
p = Pipeline.find(self.qc_pipeline_id)
-
self.update_attributes!(:qc_pipeline_id => p.next_pipeline_id, :qc_state => 'qc_manual')
-
end
-
end
-
-
1
def qc_manual_in_progress
-
self.qc_state = "qc_manual_in_progress"
-
self.save
-
end
-
-
1
def qc_pipeline_update
-
self.qc_pipeline = Pipeline.find_by_name_and_automated("quality control", true)
-
self.qc_state = "qc_pending"
-
end
-
1
private :qc_pipeline_update
-
-
# POST /batches/submit_to_qc_queue/:id.xml
-
1
def submit_to_qc_queue
-
logger.debug "Batch #{self.id} attempting to be added to QC queue. State is #{self.qc_state}"
-
# Get QC workflow and its tasks
-
workflow = LabInterface::Workflow.find_by_name("quality control", :include => [:tasks])
-
tasks = workflow.tasks
-
if self.qc_state == "qc_pending"
-
# Submit requests for all tasks in the workflow
-
tasks.each do |task|
-
# Constructing the XML file to use in sending the request
-
h_doc = {}
-
self.batch_requests.each do |b_request|
-
h_doc["lane_#{b_request.position}"] = b_request.id
-
end
-
h_doc["task_id"] = task.id
-
h_doc["batch"] = self.id
-
h_doc["keys"] = {}
-
task.descriptors.each do |t|
-
h_doc["keys"]["#{t.key}"] = t.value
-
end
-
doc = h_doc.to_xml(:root => "criteria", :skip_types => true)
-
# A *Hacky* solution to get the XML readable for Chainlink
-
doc = doc.to_s.gsub!('-', '_').gsub!('UTF_8', 'UTF-8')
-
# logger.debug doc
-
# Publishing the request to AMQ
-
publish :qc_requests, doc
-
end
-
self.qc_submitted
-
return true
-
else
-
return false
-
end
-
end
-
-
# Format qc_info on batch for JSON
-
1
def formatted_batch_qc_details
-
batch = self
-
temp_variable = {}
-
excluding_descriptors = %w(location item batch check jobs run_meta check_key)
-
-
if batch.requests
-
batch.requests.each do |item|
-
temp_variable["#{item}"] = item
-
if batch.qc_pipeline.previous_pipeline
-
batch.qc_pipeline.previous_pipeline.workflow.tasks.each do |task|
-
item.lab_events.each do |event|
-
if event.description == task.name
-
grouped_descriptors = {}
-
grouped_descriptors["#{task.name}"] = {}
-
event.descriptors.each do |descriptor|
-
unless excluding_descriptors.include? descriptor.name
-
grouped_descriptors["#{task.name}"]["#{descriptor.name}"] = descriptor.value
-
end
-
end
-
temp_variable["#{item}"]["qc_data"] = grouped_descriptors
-
end
-
end
-
end
-
end
-
end
-
end
-
batch["items"] = temp_variable
-
batch
-
end
-
-
1
private
-
-
1
def assets_qc_tasks_results
-
auto_qc_pipeline = Pipeline.first(:conditions => {:name => "quality control", :automated => true})
-
qc_workflow = LabInterface::Workflow.find_by_pipeline_id auto_qc_pipeline.id
-
qc_tasks = qc_workflow.tasks
-
results = []
-
qc_tasks.each do |task|
-
self.requests.each do |request|
-
if request.asset && request.asset.resource.nil?
-
results << request.has_passed(self, task)
-
end
-
end
-
end
-
return results
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module SequencingQcPipeline
-
# Returns Quality Control pipeline found using Regexp
-
1
def qc_auto_pipeline_id
-
Pipeline.first(:conditions => {:name => "quality control", :automated => true}).id
-
end
-
-
1
def self.qc_auto_pipeline
-
Pipeline.first(:conditions => {:name => "quality control", :automated => true})
-
end
-
-
1
def cluster_formation_pipeline_id
-
Pipeline.find_all_by_sti_type("SequencingPipeline").map(&:id)
-
end
-
-
1
def qc_pending_auto_batches
-
Batch.find(:all,
-
:conditions => ["state = ? AND qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "released", "qc_pending", self.id, cluster_formation_pipeline_id],
-
:limit => 20, :order => "created_at DESC", :include => [:user])
-
end
-
-
1
def qc_pending_manual_batches
-
# {:state => "released", :qc_state => "qc_manual", :qc_pipeline_id => self.id},
-
Batch.find(:all,
-
:conditions => ["qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "qc_manual", self.id, cluster_formation_pipeline_id],
-
:order => "created_at DESC", :include => [:user])
-
end
-
-
1
def qc_in_progress_auto_batches
-
# {:state => "released", :qc_state => "qc_criteria_received", :qc_pipeline_id => self.id},
-
Batch.find(:all,
-
:conditions => ["qc_state = ? OR qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "qc_submitted", "qc_criteria_received", self.id, cluster_formation_pipeline_id],
-
:order => "created_at DESC", :limit => 20, :include => [:user])
-
end
-
-
1
def qc_submitted_to_qc_batches
-
Batch.find(:all,
-
:conditions => ["qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "qc_submitted", self.id, cluster_formation_pipeline_id],
-
:order => "created_at DESC", :limit => 20, :include => [:user])
-
end
-
-
1
def qc_in_progress_manual_batches
-
# {:state => "started", :qc_state => "qc_manual", :qc_pipeline_id => self.id},
-
Batch.find(:all,
-
:conditions => ["qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "qc_manual_in_progress", self.id, cluster_formation_pipeline_id],
-
:order => "created_at DESC", :limit => 20, :include => [:user])
-
end
-
-
1
def qc_completed_auto_batches
-
# {:state => "released", :qc_state => "qc_manual", :qc_pipeline_id => self.next_pipeline_id},
-
Batch.find(:all,
-
:conditions => ["qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "qc_manual", self.next_pipeline_id, cluster_formation_pipeline_id],
-
:order => "created_at DESC", :limit => 20, :include => [:user])
-
end
-
-
1
def qc_completed_manual_batches
-
# {:state => "released", :qc_state => "qc_completed", :qc_pipeline_id => self.id},
-
Batch.find(:all,
-
:conditions => ["qc_state = ? AND qc_pipeline_id = ? AND pipeline_id in (?)", "qc_completed", self.id, cluster_formation_pipeline_id],
-
:order => "created_at DESC", :limit => 20, :include => [:user])
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
Study.find_each do |study|
-
study_report = StudyReport.create!(:study => study)
-
# delayed job
-
study_report.perform
-
end
-
-
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
-
# Provides a means of serializing and serializing submission templates.
-
# Example serialization:
-
# {
-
# :name=>"Template Name",
-
# :submission_class_name=>"LinearSubmission",
-
# :superceded_at=>nil,
-
# :product_line=>"Product Line Name",
-
# :product_catalogue=>"Product Catalogue Name",
-
# :submission_parameters=>{
-
# :request_types=>["library_creation", "sequencing"],
-
# :workflow=>"short_read_sequencing",
-
# :order_role=>"ILC"
-
# }
-
# }
-
-
module SubmissionSerializer
-
-
STRAIGHT_CLONE = ['name','submission_class_name']
-
SP_STRAIGHT_CLONE = [:info_differential,:asset_input_methods,:request_options]
-
-
def self.serialize(st)
-
attributes = st.attributes
-
new_attributes = {}
-
-
STRAIGHT_CLONE.each do |key|
-
new_attributes[key.to_sym] = attributes[key].duplicable? ? attributes[key].dup : attributes[key]
-
end
-
-
new_attributes[:product_line] = ProductLine.find(attributes['product_line_id']).name if attributes['product_line_id']
-
new_attributes[:product_catalogue] = ProductCatalogue.find(attributes['product_catalogue_id']).name if attributes['product_catalogue_id']
-
new_attributes[:superceded_by] = SubmissionTemplate.find(attributes['superceded_by_id']).name if attributes['superceded_by_id'] > 0
-
new_attributes[:superceded_by_id] = attributes['superceded_by_id']
-
new_attributes[:superceded_at] = attributes['superceded_at'].to_s if attributes['superceded_at']
-
-
sp = attributes['submission_parameters'] || {}
-
ensp = new_attributes[:submission_parameters] = {}
-
-
SP_STRAIGHT_CLONE.each do |key|
-
ensp[key] = sp[key] if sp[key].present?
-
end
-
-
if ensp[:request_options] && ensp[:request_options][:initial_state]
-
new_initial = Hash[ensp[:request_options][:initial_state].map {|k,v| [RequestType.find(k).key,v] }]
-
ensp[:request_options][:initial_state] = new_initial
-
end
-
-
ensp[:request_types] = sp[:request_type_ids_list].flatten.map {|id| RequestType.find(id).key }
-
ensp[:workflow] = Submission::Workflow.find(sp[:workflow_id]).key if sp[:workflow_id]
-
ensp[:order_role] = Order::OrderRole.find(sp[:order_role_id]).role if sp[:order_role_id]
-
-
new_attributes
-
end
-
-
def self.construct!(hash)
-
-
st = {}
-
-
STRAIGHT_CLONE.each do |key|
-
st[key.to_sym] = hash[key.to_sym]
-
end
-
-
st[:product_line_id] = ProductLine.find_or_create_by_name!(hash[:product_line]).id if hash[:product_line]
-
st[:product_catalogue_id] = ProductCatalogue.find_or_create_by_name!(hash[:product_catalogue]).id if hash[:product_catalogue]
-
st[:superceded_by_id] = hash.has_key?(:superceded_by) ? SubmissionTemplate.find_by_name(hash[:superceded_by]).try(:id)||-2 : hash[:superceded_by_id]||-1
-
st[:superceded_at] = DateTime.parse(hash[:superceded_at]) if hash.has_key?(:superceded_at)
-
-
sp = st[:submission_parameters] = {}
-
ensp = hash[:submission_parameters]
-
-
SP_STRAIGHT_CLONE.each do |key|
-
sp[key] = ensp[key] if ensp[key].present?
-
end
-
-
if sp[:request_options] && sp[:request_options][:initial_state]
-
new_initial = Hash[sp[:request_options][:initial_state].map {|k,v| [RequestType.find_by_key(k).id,v] }]
-
sp[:request_options][:initial_state] = new_initial
-
end
-
-
sp[:request_type_ids_list] = ensp[:request_types].map {|rtk| [ RequestType.find_by_key!(rtk).id ] }
-
sp[:workflow_id] = Submission::Workflow.find_by_key!(ensp[:workflow]).id if ensp[:workflow]
-
sp[:order_role_id] = Order::OrderRole.find_or_create_by_role(ensp[:order_role]).id if ensp[:order_role]
-
-
SubmissionTemplate.create!(st)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2012 Genome Research Ltd.
-
module SubmissionTemplateMaker
-
def make_new_templates!(product_line, old_template)
-
ActiveRecord::Base.transaction do
-
submission_parameters = old_template.submission_parameters.dup
-
-
submission_parameters[:request_type_ids_list] = new_request_types(
-
product_line,
-
submission_parameters[:request_type_ids_list]
-
)
-
-
SubmissionTemplate.create!(
-
{
-
:name => "#{product_line.name} - #{old_template.name}",
-
:submission_parameters => submission_parameters,
-
:product_line_id => product_line.id,
-
:visible => true
-
}.reverse_merge(old_template.attributes).except!('created_at','updated_at')
-
)
-
-
old_template.update_attributes(:visible => false)
-
end
-
end
-
-
def new_request_type(product_line, old_request_type_id_arr)
-
# Remember to pull the id out of the wrapping array...
-
old_request_type = RequestType.find(old_request_type_id_arr.first)
-
-
new_key = "#{product_line.name.underscore}_#{old_request_type.key}"
-
-
RequestType.find_by_key(new_key) or
-
raise "New RequestType '#{new_key}' not found"
-
end
-
-
def new_request_types(product_line, old_request_types_list)
-
old_request_types_list.map do |old_rtype|
-
[ new_request_type(product_line, old_rtype).id ]
-
end
-
end
-
end
-
#!/usr/bin/env ruby
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
# This script is massively faster than using grep as it allows us to do a single simple reg ex.
-
filtered_tables = ["aliquots", "archived_properties", "asset_audits", "asset_barcodes", "asset_creation_parents", "asset_creations", "asset_group_assets", "asset_groups", "asset_links", "assets", "attachments", "audits", "batch_requests", "batches", "billing_events", "bulk_transfers", "comments", "container_associations", "db_files", "delayed_jobs", "depricated_attempts", "documents", "documents_shadow", "events", "external_properties", "faculty_sponsors", "failures", "identifiers", "implements", "items", "lab_events", "lane_metadata", "location_associations", "lots", "orders", "pac_bio_library_tube_metadata", "permissions", "plate_conversions", "plate_metadata", "plate_owners", "plate_volumes", "pre_capture_pool_pooled_requests", "pre_capture_pools", "project_metadata", "projects", "qc_decision_qcables", "qc_decisions", "qc_files", "qcable_creators", "qcables", "quotas_bkp", "request_events", "request_informations", "request_metadata", "request_quotas_bkp", "requests", "sample_manifests", "sample_metadata", "sample_registrars", "samples", "sanger_sample_ids", "specific_tube_creation_purposes", "stamp_qcables", "stamps", "state_changes", "studies", "study_metadata", "study_reports", "study_samples", "study_samples_backup", "submissions", "submitted_assets", "tag_layouts", "transfers", "tube_creation_children", "uuids", "well_attributes", "well_links", "well_to_tube_transfers", "workflow_samples"]
-
-
while line = gets
-
match = /^INSERT INTO `([^`]+)`/.match(line)
-
next if match && filtered_tables.include?(match[1])
-
puts line
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2012,2013,2015 Genome Research Ltd.
-
1
module Sanger
-
1
module Robots
-
1
module Tecan
-
1
class Generator
-
1
def self.mapping(data_object, total_volume)
-
raise ArgumentError, "data_object needs to conform to an interface. WIP" if data_object.nil?
-
-
dest_barcode_index = barcode_to_plate_index(data_object["destination"])
-
-
source_barcode_index = source_barcode_to_plate_index(data_object["destination"])
-
buffer_data = buffers(data_object, total_volume)
-
output_file_contents = [ header(data_object), dyn_mappings(data_object)]
-
unless buffer_data.blank?
-
output_file_contents << buffer_seperator
-
output_file_contents << buffer_data
-
end
-
output_file_contents << footer(source_barcode_index,dest_barcode_index)
-
-
output_file_contents.join("\n").gsub(/\n\n/,"\n")
-
end
-
-
1
def self.barcode_to_plate_index(plates)
-
barcode_lookup = {}
-
plates.each_with_index do |plate,index|
-
barcode_lookup[plate[0]]= index+1
-
end
-
barcode_lookup
-
end
-
-
1
def self.source_barcode_to_plate_index(destination)
-
all_barcodes = []
-
barcode_lookup = {}
-
destination.each do |plate_id, plate_info|
-
plate_info["mapping"].each do |map_well|
-
well = map_well["src_well"]
-
all_barcodes << well[0]
-
end
-
end
-
all_barcodes.uniq.each_with_index do |plate,index|
-
barcode_lookup[plate]= index+1
-
end
-
barcode_lookup
-
end
-
-
1
private
-
1
def self.header(data_object)
-
"C;\nC; This file created by #{data_object["user"]} on #{data_object["time"]}\nC;"
-
end
-
-
1
def self.tecan_precision_value(value)
-
"%.#{configatron.tecan_precision}f" % value
-
end
-
-
1
def self.each_mapping(data_object)
-
data_object["destination"].each do |dest_plate_barcode,plate_details|
-
mapping_by_well = Hash.new {|h,i| h[i] = [] }
-
plate_details["mapping"].each do |mapping|
-
destination_position = Map::Coordinate.description_to_vertical_plate_position(mapping["dst_well"],plate_details["plate_size"])
-
mapping_by_well[destination_position] << mapping
-
end
-
-
mapping_by_well.sort{|a,b| a[0]<=>b[0]}.each do |dest_position, mappings|
-
mappings.each do |mapping|
-
yield(mapping, dest_plate_barcode, plate_details)
-
end
-
end
-
end
-
end
-
-
1
def self.dyn_mappings(data_object)
-
dyn_mappings = ""
-
each_mapping(data_object) do |mapping,dest_plate_barcode,plate_details|
-
source_barcode = "#{mapping["src_well"][0]}"
-
source_name = data_object["source"]["#{mapping["src_well"][0]}"]["name"]
-
source_position = Map::Coordinate.description_to_vertical_plate_position(mapping["src_well"][1],data_object["source"]["#{mapping["src_well"][0]}"]["plate_size"])
-
destination_position = Map::Coordinate.description_to_vertical_plate_position(mapping["dst_well"],plate_details["plate_size"])
-
temp = [
-
"A;#{source_barcode};;#{source_name};#{source_position};;#{tecan_precision_value(mapping['volume'])}",
-
"D;#{dest_plate_barcode};;#{plate_details["name"]};#{destination_position};;#{tecan_precision_value(mapping['volume'])}" ,
-
"W;\n"].join("\n")
-
dyn_mappings += temp
-
end
-
dyn_mappings
-
end
-
-
1
def self.buffer_seperator
-
"C;"
-
end
-
-
1
def self.buffers(data_object, total_volume)
-
buffer = []
-
each_mapping(data_object) do |mapping,dest_plate_barcode,plate_details|
-
if total_volume > mapping["volume"]
-
dest_name = data_object["destination"][dest_plate_barcode]["name"]
-
volume = [(total_volume - mapping["volume"]),configatron.tecan_minimum_volume].max
-
vert_map_id = Map::Coordinate.description_to_vertical_plate_position(mapping["dst_well"],plate_details["plate_size"])
-
buffer << "A;BUFF;;96-TROUGH;#{vert_map_id};;#{tecan_precision_value(volume)}\nD;#{dest_plate_barcode};;#{dest_name};#{vert_map_id};;#{tecan_precision_value(volume)}\nW;"
-
end
-
end
-
buffer.join("\n")
-
end
-
-
1
def self.footer(source_barcode_index,dest_barcode_index)
-
footer = "C;\n"
-
source_barcode_index.sort{|a,b| a[1]<=>b[1]}.each do |barcode,index|
-
footer += "C; SCRC#{index} = #{barcode}\n"
-
end
-
footer += "C;\n"
-
dest_barcode_index.sort{|a,b| a[1]<=>b[1]}.each do |barcode,index|
-
footer += "C; DEST#{index} = #{barcode}\n"
-
end
-
footer
-
end
-
end
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2015 Genome Research Ltd.
-
module TubePurposeHelper
-
-
class PurposeMigrator
-
attr_reader :purposes, :migration
-
delegate :say, :to => :migration
-
def initialize(purposes,migration)
-
@purposes = purposes
-
@migration = migration
-
end
-
-
def to(target_purpose)
-
purposes.each do |name|
-
say "Updating #{name}..."
-
Purpose.find_by_name(name).child_relationships.first.update_attributes!(:child=>target_purpose)
-
end
-
end
-
end
-
-
class RequestTypeMigrator
-
attr_reader :request_types, :migration
-
delegate :say, :to => :migration
-
def initialize(request_types,migration)
-
@request_types = request_types
-
@migration = migration
-
end
-
-
def to(target_purpose)
-
request_types.each do |key|
-
say "Updating #{key}..."
-
RequestType.find_by_key!(key).update_attributes!(:target_purpose_id=>target_purpose.id)
-
end
-
end
-
-
def repair_data(old_purpose,new_purpose)
-
request_types.each do |key|
-
say "Repairing #{key}..."
-
rt = RequestType.find_by_key!(key)
-
updated = 0
-
MultiplexedLibraryTube.find_each(
-
:select => 'DISTINCT assets.*',
-
:joins => 'LEFT JOIN requests ON requests.target_asset_id = assets.id',
-
:conditions => ['assets.plate_purpose_id = ? AND request_type_id = ?',old_purpose.id,rt.id]
-
) do |library_tube|
-
library_tube.update_attributes!(:purpose=>new_purpose)
-
updated += 1
-
end
-
say "#{updated} assets updated"
-
end
-
end
-
end
-
-
def migrate_purposes(*purposes)
-
PurposeMigrator.new(purposes,self)
-
end
-
-
def migrate_request_types(*request_types)
-
RequestTypeMigrator.new(request_types,self)
-
end
-
-
def new_illumina_mx_tube(name)
-
say "Creating '#{name}'' purpose"
-
IlluminaHtp::MxTubeNoQcPurpose.create!(
-
:name => name,
-
:target_type => 'MultiplexedLibraryTube',
-
:qc_display => false,
-
:can_be_considered_a_stock_plate => false,
-
:default_state => 'pending',
-
:barcode_printer_type => BarcodePrinterType.find_by_name('1D Tube'),
-
:cherrypickable_target => false,
-
:cherrypickable_source => false,
-
:cherrypick_direction => 'column',
-
:barcode_for_tecan => 'ean13_barcode'
-
)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
accession_needed = []
-
Study.all.each do |study|
-
next unless study.data_release_strategy == "open"
-
next unless study.ena_accession_required?
-
next if study.samples.nil?
-
-
study.samples.each do |sample|
-
if sample.ebi_accession_number
-
accession_needed << sample.id
-
end
-
end
-
end
-
-
app = ActiveResource::Connection.new("http://#{configatron.site_url}")
-
accession_needed.each do |sample_id|
-
puts "#{sample_id}"
-
sample = Sample.find(sample_id)
-
next if sample.nil?
-
if sample.ebi_accession_number
-
begin
-
app.get "/samples/accession/#{sample_id}?api_key=#{configatron.accession_local_key}"
-
rescue
-
end
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2011,2012 Genome Research Ltd.
-
def open_barcode_stream(&block)
-
user = "read_only"
-
password = "read_only"
-
connection = "cas"
-
open "|sqlplus -S #{user}/#{password}@#{connection}", "r+" do |f|
-
f.print <<-EOS
-
SET PAGES 0
-
SELECT id_person, email
-
FROM PERSON
-
WHERE iscurrent = 1
-
;
-
EOS
-
-
block.call(f)
-
end
-
end
-
-
def read_user_id
-
user_barcode = {}
-
open_barcode_stream do |f|
-
while l = f.gets
-
l.strip!
-
break if /rows selected/i =~l
-
break if /^$/ =~l
-
raise Exception.new, "'#{l}' is not a valid id" unless /^\d+$/ =~l
-
id = l.to_i
-
user = f.gets.strip
-
-
user_barcode[user]=id
-
puts "#{user} => #{id}"
-
-
f.gets #read a blamk lien as record separator
-
end
-
-
f.close
-
user_barcode
-
-
end
-
end
-
def update_user
-
user_to_id = read_user_id
-
puts "User number #{User.all.size}"
-
User.all.each do |user|
-
barcode_id = user_to_id[user.login]
-
if barcode_id
-
barcode = Barcode.barcode_to_human(Barcode.calculate_barcode("ID", barcode_id))
-
if barcode != user.barcode
-
puts "assigning new barcode '#{barcode_id}' to user '#{user.login}'"
-
user.barcode = barcode
-
user.save
-
end
-
end
-
end
-
end
-
-
ActiveRecord::Base.transaction do
-
update_user
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
class UuidMigration < ActiveRecord::Migration
-
class_attribute :model_to_migrate
-
-
def self.up
-
Uuid.transaction do
-
count = 0
-
-
model_to_migrate.find_in_batches do |batch|
-
say_with_time("Generating #{model_to_migrate.name} UUIDs #{count}-#{count+batch.size-1} ...") do
-
Uuid.import(
-
[ 'resource_type', 'resource_id', 'external_id' ],
-
batch.map { |record| [ (record.respond_to?(:sti_type) ? record.sti_type : record.class.name), record.id, Uuid.generate_uuid ] }
-
)
-
end
-
-
count += batch.size
-
end
-
end
-
end
-
-
def self.down
-
# Not really anything we should be doing here, except maybe deleting all of the UUIDs?
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
1
module Validateable
-
-
4
[:save, :save!, :update_attribute].each{|attr| define_method(attr){}}
-
-
1
def method_missing(symbol, *params)
-
if(symbol.to_s =~ /(.*)_before_type_cast$/)
-
send($1)
-
end
-
end
-
-
1
def self.append_features(base)
-
4
super
-
4
base.send(:include, ActiveRecord::Validations)
-
4
base.extend ClassMethods
-
end
-
-
1
def validate!
-
raise(ActiveRecord::RecordInvalid, self) unless valid?
-
end
-
-
1
module ClassMethods
-
1
def self_and_descendants_from_active_record
-
[self]
-
end
-
1
def human_attribute_name(attribute_key_name, options = {})
-
defaults = self_and_descendants_from_active_record.map do |klass|
-
"#{klass.name.underscore}.#{attribute_key_name}""#{klass.name.underscore}.#{attribute_key_name}"
-
end
-
defaults << options[:default] if options[:default]
-
defaults.flatten!
-
defaults << attribute_key_name.to_s.humanize
-
options[:count] ||= 1
-
I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
-
end
-
1
def human_name(options = {})
-
defaults = self_and_descendants_from_active_record.map do |klass|
-
"#{klass.name.underscore}""#{klass.name.underscore}"
-
end
-
defaults << self.name.humanize
-
I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options))
-
end
-
end
-
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2014 Genome Research Ltd.
-
module ViewsSchema
-
-
def self.each_view
-
all_views.each do |name|
-
query = ActiveRecord::Base.connection.execute("SHOW CREATE TABLE #{name}")
-
if query.respond_to?(:fetch_hash)
-
query.fetch_hash["Create View"].gsub(/DEFINER=`[^`]*`@`[^`]*` /,'')
-
else
-
definition = query.first["Create View"].gsub(/DEFINER=`[^`]*`@`[^`]*` /,'')
-
end
-
yield(name,definition)
-
end
-
rescue ActiveRecord::StatementInvalid => exception
-
puts %Q{\e[1;31m
-
==============================================================
-
* WARNING! *
-
* The attempt to dump the view schema failed. *
-
* It is likely that your migrations have broken one or more *
-
* of the views. It is CRITICAL that this problem is *
-
* addressed before you commit these migrations. *
-
* To ensure that reporting is not affected please ensure *
-
* that the updated view accurately reflects the data. *
-
* DO NOT change the schema of the view, merely how it *
-
* retrieves the data. Ensure the changes are thoroughly *
-
* tested against production like data. *
-
* *
-
* Downstream users should be notified of potential *
-
* disruption. *
-
==============================================================
-
\e[0m}
-
raise exception
-
end
-
-
def self.all_views
-
ActiveRecord::Base.connection.execute(%Q{
-
SELECT TABLE_NAME AS name
-
FROM INFORMATION_SCHEMA.VIEWS
-
WHERE TABLE_SCHEMA = '#{ActiveRecord::Base.connection.current_database}';}
-
).map do |v|
-
# Behaviour depends on ruby version, so we need to work out what we have
-
v.is_a?(Hash) ? v['name'] : v.first
-
end.flatten
-
end
-
-
def self.create_view(name,definition)
-
ActiveRecord::Base.connection.execute(definition)
-
end
-
-
def self.update_view(name,definition)
-
raise "Invalid name" unless /^[a-z0-9_]*$/===name
-
ActiveRecord::Base.connection.execute("DROP VIEW IF EXISTS `#{name}`;")
-
create_view(name,definition)
-
end
-
end
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011 Genome Research Ltd.
-
PlateVolume.process_all_volume_check_files
-
#This file is part of SEQUENCESCAPE is distributed under the terms of GNU General Public License version 1 or later;
-
#Please refer to the LICENSE and README files for information on licensing and authorship of this file.
-
#Copyright (C) 2007-2011,2015 Genome Research Ltd.
-
1
module Workflowed
-
1
def self.included(base)
-
4
base.class_eval do
-
4
belongs_to :workflow, :class_name => "Submission::Workflow"
-
4
scope :for_workflow, ->(workflow) { {:conditions=>{:workflow_id=>workflow} } }
-
end
-
end
-
end